From 7d51deac4e5fd97c813c5c23a366a559913f5262 Mon Sep 17 00:00:00 2001 From: howsohazard <143410553+howsohazard@users.noreply.github.com> Date: Tue, 25 Jun 2024 17:31:52 -0400 Subject: [PATCH] 20490: Unifies .nas, and .nan under null keyword, MAJOR (#155) .nas and .nan have been removed and have been replaced simply by (null) --------- Co-authored-by: Cade Mack <24661281+cademack@users.noreply.github.com> --- docs/index.html | 2 +- docs/language.js | 10 +- src/Amalgam/GeneralizedDistance.h | 12 +- src/Amalgam/Opcodes.cpp | 3 +- src/Amalgam/Opcodes.h | 4 +- src/Amalgam/Parser.cpp | 33 +- src/Amalgam/SBFDSColumnData.h | 116 ++---- src/Amalgam/SeparableBoxFilterDataStore.cpp | 46 +-- src/Amalgam/SeparableBoxFilterDataStore.h | 7 +- src/Amalgam/amlg_code/full_test.amlg | 49 ++- src/Amalgam/entity/EntityQueryBuilder.h | 20 +- src/Amalgam/evaluablenode/EvaluableNode.cpp | 110 +++--- src/Amalgam/evaluablenode/EvaluableNode.h | 167 ++++---- .../evaluablenode/EvaluableNodeManagement.h | 31 +- .../EvaluableNodeTreeFunctions.cpp | 10 +- .../EvaluableNodeTreeFunctions.h | 4 +- .../EvaluableNodeTreeManipulation.cpp | 27 +- .../EvaluableNodeTreeManipulation.h | 2 +- src/Amalgam/importexport/FileSupportCSV.cpp | 2 +- src/Amalgam/importexport/FileSupportYAML.cpp | 9 +- src/Amalgam/interpreter/Interpreter.cpp | 6 +- src/Amalgam/interpreter/Interpreter.h | 2 +- .../interpreter/InterpreterOpcodesBase.cpp | 32 +- .../InterpreterOpcodesCodeMixing.cpp | 8 +- .../InterpreterOpcodesDataTypes.cpp | 15 +- .../InterpreterOpcodesListManipulation.cpp | 3 - .../interpreter/InterpreterOpcodesLogic.cpp | 16 +- .../InterpreterOpcodesTransformations.cpp | 26 +- src/Amalgam/out.txt | 371 +++++++++--------- src/Amalgam/string/StringManipulation.cpp | 2 +- 30 files changed, 522 insertions(+), 623 deletions(-) diff --git a/docs/index.html b/docs/index.html index c2b88430..1faf8e7f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -352,7 +352,7 @@

Language syntax

(call (retrieve_from_contained_entity "MyLibrary" "MyFunction") (assoc parameter_a 1 parameter_b 2))

- Numbers are represented via numbers, as well as ".", "-", and "e" for base-ten exponents. Further, infinity and negative infinity are represented as ".infinity" and "-.infinity" respectively, and not-a-number is represented as ".nan". The special null value with string type, not-a-string, is represented as ".nas". + Numbers are represented via numbers, as well as ".", "-", and "e" for base-ten exponents. Further, infinity and negative infinity are represented as ".infinity" and "-.infinity" respectively. Not-a-number and non-string results are represented via the opcode (null).

All regular expressions are EMCA-standard regular expressions. See https://en.cppreference.com/w/cpp/regex/ecmascript or https://262.ecma-international.org/5.1/#sec-15.10 for further details on syntax. diff --git a/docs/language.js b/docs/language.js index 6a29564d..b6afd6e9 100644 --- a/docs/language.js +++ b/docs/language.js @@ -408,7 +408,7 @@ var data = [ { "parameter" : "generalized_distance list|assoc|number weights list|assoc distance_types list|assoc attributes list|assoc|number deviations number p_value list|assoc|* vector1 [list|assoc|* vector2] [list value_names] [bool surprisal_space]", "output" : "number", - "description" : "Computes the generalized norm between vector1 and vector2 (or an equivalent zero vector if unspecified) with parameter specified by the p_value (2 being Euclidian distance), using the numerical distance or edit distance as appropriate. The parameter value_names, if specified as a list of the names of the values, will transform via unzipping any assoc into a list for the respective parameter in the order of the value_names, or if a number will use the number repeatedly for every element. weights is a list of dimension weights to use for the query, each value mapping to its respective element in the vectors. If weights is null, then it will assume that the weights are 1 and additionally will ignore null values for the vectors instead of treating them as unknown differences. The parameter distance_types is either a list strings or an assoc of strings indicating the type of distance for each feature. Allowed values are \"nominal_numeric\", \"nominal_string\", \"nominal_code\", \"continuous_numeric\", \"continuous_numeric_cyclic\", \"continuous_string\", and \"continuous_code\". Nominals evaluate whether the two values are the same and continuous evaluates the difference between the two values. The numeric, string, or code modifier specifies how the difference is measured, and cyclic means it is a difference that wraps around. \nFor attributes, the particular distance_types specifies what particular attributes are expected. For a nominal distance_type, a number indicates the nominal count, whereas null will infer from the values given. Cyclic requires a single value, which is the upper bound of the difference for the cycle range (e.g., if the value is 360, then the supremum difference between two values will be 360, leading 1 and 359 to have a difference of 2).\n Deviations are used during distance calculation to specify uncertainty per-element, the minimum difference between two values prior to exponentiation. Specifying null as a deviation is equivalent to setting each deviation to 0. Each deviation for each feature can be a single value or a list. If it is a single value, that value is used as the deviation and differences and deviations for null values will automatically computed from the data based on the maximum difference. If a deviation is provided as a list, then the first value is the deviation, the second value is the difference to use when one of the values being compared is null, and the third value is the difference to use when both of the values are null. If the third value is omitted, it will use the second value for both. If both of the null values are omitted, then it will compute the maximum difference and use that for both. For nominal types, the value for each feature can be a numeric deviation, an assoc, or a list. If the value is an assoc it specifies deviation information, where each key of the assoc is the nominal value, and each value of the assoc can be a numeric deviation value, a list, or an assoc, with the list specifying either an assoc followed optionally by the default deviation. This inner assoc, regardless of whether it is in a list, maps the value to each actual value's deviation. If any vector value is null or evaluates to nan, or any of the differences between vector1 and vector2 evaluate to null or nan, then it will compute a corresponding maximum distance value based on the properties of the feature. If surprisal space is true, which defaults to false, it will perform all computations in surprisal space.", + "description" : "Computes the generalized norm between vector1 and vector2 (or an equivalent zero vector if unspecified) with parameter specified by the p_value (2 being Euclidian distance), using the numerical distance or edit distance as appropriate. The parameter value_names, if specified as a list of the names of the values, will transform via unzipping any assoc into a list for the respective parameter in the order of the value_names, or if a number will use the number repeatedly for every element. weights is a list of dimension weights to use for the query, each value mapping to its respective element in the vectors. If weights is null, then it will assume that the weights are 1 and additionally will ignore null values for the vectors instead of treating them as unknown differences. The parameter distance_types is either a list strings or an assoc of strings indicating the type of distance for each feature. Allowed values are \"nominal_numeric\", \"nominal_string\", \"nominal_code\", \"continuous_numeric\", \"continuous_numeric_cyclic\", \"continuous_string\", and \"continuous_code\". Nominals evaluate whether the two values are the same and continuous evaluates the difference between the two values. The numeric, string, or code modifier specifies how the difference is measured, and cyclic means it is a difference that wraps around. \nFor attributes, the particular distance_types specifies what particular attributes are expected. For a nominal distance_type, a number indicates the nominal count, whereas null will infer from the values given. Cyclic requires a single value, which is the upper bound of the difference for the cycle range (e.g., if the value is 360, then the supremum difference between two values will be 360, leading 1 and 359 to have a difference of 2).\n Deviations are used during distance calculation to specify uncertainty per-element, the minimum difference between two values prior to exponentiation. Specifying null as a deviation is equivalent to setting each deviation to 0. Each deviation for each feature can be a single value or a list. If it is a single value, that value is used as the deviation and differences and deviations for null values will automatically computed from the data based on the maximum difference. If a deviation is provided as a list, then the first value is the deviation, the second value is the difference to use when one of the values being compared is null, and the third value is the difference to use when both of the values are null. If the third value is omitted, it will use the second value for both. If both of the null values are omitted, then it will compute the maximum difference and use that for both. For nominal types, the value for each feature can be a numeric deviation, an assoc, or a list. If the value is an assoc it specifies deviation information, where each key of the assoc is the nominal value, and each value of the assoc can be a numeric deviation value, a list, or an assoc, with the list specifying either an assoc followed optionally by the default deviation. This inner assoc, regardless of whether it is in a list, maps the value to each actual value's deviation. If any vector value is null or any of the differences between vector1 and vector2 evaluate to null, then it will compute a corresponding maximum distance value based on the properties of the feature. If surprisal space is true, which defaults to false, it will perform all computations in surprisal space.", "example" : "(print (generalized_distance 0.01 (null) (null) (list null (list 0 360)) (list 0.5 0.0) (list 0 2 3) (list 1 2 3)))\n(print (generalized_distance 0.01 (list 0.25 0.25 0.5) (null) (null) (null) (list 1 2 3) (list 0 2 3) ))\n(generalized_distance 1 (list 0.3333 0.3333 0.3333) (list 5 0) (null) (null) (list 1 2 3) (list 10 2 10) )" }, @@ -498,7 +498,7 @@ var data = [ "new value" : "new", "concurrency" : true, "new target scope": true, - "description" : "For each element in the collection, pushes a new target scope onto the stack, so that current_value accesses the element in the list and current_index accesses the list or assoc index, with target representing the original list or assoc, and evaluates the function. If function evaluates to true, then the element is put in a new list or assoc (matching the input type) that is returned. If function is omitted, then it will remove any elements in the collection that are null, .nan, or .nas string.", + "description" : "For each element in the collection, pushes a new target scope onto the stack, so that current_value accesses the element in the list and current_index accesses the list or assoc index, with target representing the original list or assoc, and evaluates the function. If function evaluates to true, then the element is put in a new list or assoc (matching the input type) that is returned. If function is omitted, then it will remove any elements in the collection that are null.", "example" : "(print (filter (lambda (> (current_value) 2)) (list 1 2 3 4)))" }, @@ -732,7 +732,7 @@ var data = [ "output" : "bool", "new value" : "new", "concurrency" : true, - "description" : "Evaluates to true if all values are equal (will recurse into data structures), false otherwise. Values of nan (not a number) are considered equal because they represent the same node, unlike many other floating point representation systems.", + "description" : "Evaluates to true if all values are equal (will recurse into data structures), false otherwise. Values of null are considered equal.", "example" : "(print (= 4 4 5))\n(print (= 4 4 4))" }, @@ -809,7 +809,7 @@ var data = [ { "parameter" : "weighted_rand [list of lists|assoc weighted_values] [number number_to_generate] [bool unique]", "output" : "*", - "description" : "Each entity has its own random stream, and if called from a sandbox, then it uses a new stream without interrupting the stream of the calling entity. If the parameter is a list, it will uniformly randomly choose and evaluate to one element of the list. If an assoc, then it will randomly evaluate to one of the keys using the values as the weights for the probabilities. Nans and negative numbers are treated as zero. Infinities are normalized as to only select from infinities in the list. If all values are 0, then they are normalized to having the same weight. If a list of lists, it will use the first list as a list of values and the second list as a list of weights and otherwise work like it would for an assoc. If number_to_generate is specified, it will generate a list of multiple values (even if number_to_generate is 1). If unique is true (it defaults to false), then it will only return unique values, the same as selecting from the list or assoc without replacement.", + "description" : "Each entity has its own random stream, and if called from a sandbox, then it uses a new stream without interrupting the stream of the calling entity. If the parameter is a list, it will uniformly randomly choose and evaluate to one element of the list. If an assoc, then it will randomly evaluate to one of the keys using the values as the weights for the probabilities. Nulls and negative numbers are treated as zero. Infinities are normalized as to only select from infinities in the list. If all values are 0, then they are normalized to having the same weight. If a list of lists, it will use the first list as a list of values and the second list as a list of weights and otherwise work like it would for an assoc. If number_to_generate is specified, it will generate a list of multiple values (even if number_to_generate is 1). If unique is true (it defaults to false), then it will only return unique values, the same as selecting from the list or assoc without replacement.", "example" : "(print (rand (list (list 1 2 4 5 7) (list 0.2 0.2 0.1 0.1 0.4))))\n(print (rand (assoc \"a\" 1 \"b\" 3))\n(print (rand (assoc \"a\" .25 \"b\" .75)) \"\\n\")\n(print (rand (assoc \"a\" .25 \"b\" .75) 4) \"\\n\")\n(print (rand (range 0 10) 10 (true)) \"\\n\")" }, @@ -1549,7 +1549,7 @@ var data = [ "parameter" : "query_mode string label_name [string weight_label_name] [bool numeric]", "output" : "query", "new value" : "new", - "description" : "When used as a query argument, finds the statistical mode of label_name for numerical data. If weight_label_name is specified, it will find the weighted mode. If numeric is true, its default, then it will treat all values as numeric, otherwise it will treat them all as strings. If numeric and no numeric mode exists, it will return .nan, but if string and no string mode exists, it will return null.", + "description" : "When used as a query argument, finds the statistical mode of label_name for numerical data. If weight_label_name is specified, it will find the weighted mode. If numeric is true, its default, then it will treat all values as numeric, otherwise it will treat them all as strings. If numeric and no numeric mode exists, it will return (null), but if string and no string mode exists, it will return null.", "example" : "(compute_on_contained_entities \"TestEntity\" (list\n (query_mode \"TargetLabel\")\n))" }, diff --git a/src/Amalgam/GeneralizedDistance.h b/src/Amalgam/GeneralizedDistance.h index c9053e83..dd45a1a7 100644 --- a/src/Amalgam/GeneralizedDistance.h +++ b/src/Amalgam/GeneralizedDistance.h @@ -373,8 +373,8 @@ class GeneralizedDistanceEvaluator __forceinline double ComputeDistanceTermNominal(EvaluableNodeImmediateValue a, EvaluableNodeImmediateValue b, EvaluableNodeImmediateValueType a_type, EvaluableNodeImmediateValueType b_type, size_t index, bool high_accuracy) { - bool a_is_null = EvaluableNodeImmediateValue::IsNullEquivalent(a_type, a); - bool b_is_null = EvaluableNodeImmediateValue::IsNullEquivalent(b_type, b); + bool a_is_null = EvaluableNodeImmediateValue::IsNull(a_type, a); + bool b_is_null = EvaluableNodeImmediateValue::IsNull(b_type, b); if(a_is_null && b_is_null) return ComputeDistanceTermUnknownToUnknown(index, high_accuracy); @@ -754,8 +754,8 @@ class GeneralizedDistanceEvaluator __forceinline double LookupNullDistanceTerm(EvaluableNodeImmediateValue a, EvaluableNodeImmediateValue b, EvaluableNodeImmediateValueType a_type, EvaluableNodeImmediateValueType b_type, size_t index, bool high_accuracy) { - bool a_unknown = EvaluableNodeImmediateValue::IsNullEquivalent(a_type, a); - bool b_unknown = EvaluableNodeImmediateValue::IsNullEquivalent(b_type, b); + bool a_unknown = EvaluableNodeImmediateValue::IsNull(a_type, a); + bool b_unknown = EvaluableNodeImmediateValue::IsNull(b_type, b); if(a_unknown && b_unknown) return ComputeDistanceTermUnknownToUnknown(index, high_accuracy); if(a_unknown || b_unknown) @@ -1245,9 +1245,9 @@ class RepeatedGeneralizedDistanceEvaluator return distEvaluator->ComputeDistanceTermNominalUniversallySymmetricExactMatch(index, high_accuracy); } - if(EvaluableNodeImmediateValue::IsNullEquivalent(other_type, other_value)) + if(EvaluableNodeImmediateValue::IsNull(other_type, other_value)) { - if(feature_data.targetValue.IsNullEquivalent()) + if(feature_data.targetValue.IsNull()) return distEvaluator->ComputeDistanceTermUnknownToUnknown(index, high_accuracy); else return distEvaluator->ComputeDistanceTermKnownToUnknown(index, high_accuracy); diff --git a/src/Amalgam/Opcodes.cpp b/src/Amalgam/Opcodes.cpp index 555ee9ba..c8462f4d 100644 --- a/src/Amalgam/Opcodes.cpp +++ b/src/Amalgam/Opcodes.cpp @@ -10,7 +10,7 @@ void StringInternPool::InitializeStaticStrings() stringToID.reserve(numStaticStrings); idToStringAndRefCount.resize(numStaticStrings); - EmplaceStaticString(ENBISI_NOT_A_STRING, ".nas"); + EmplaceStaticString(ENBISI_NOT_A_STRING, "(null)"); EmplaceStaticString(ENBISI_EMPTY_STRING, ""); @@ -293,7 +293,6 @@ void StringInternPool::InitializeStaticStrings() //end opcodes //built-in common values - EmplaceStaticString(ENBISI_nan, ".nan"); EmplaceStaticString(ENBISI_infinity, ".infinity"); EmplaceStaticString(ENBISI_neg_infinity, "-.infinity"); EmplaceStaticString(ENBISI_zero, "0"); diff --git a/src/Amalgam/Opcodes.h b/src/Amalgam/Opcodes.h index f2ff46aa..15f9419e 100644 --- a/src/Amalgam/Opcodes.h +++ b/src/Amalgam/Opcodes.h @@ -509,9 +509,7 @@ enum EvaluableNodeBuiltInStringId //leave space for ENT_ opcodes, start at the end //built-in common values - ENBISI_nas = NUM_VALID_ENT_OPCODES + NUM_ENBISI_SPECIAL_STRING_IDS, - ENBISI_nan, - ENBISI_infinity, + ENBISI_infinity = NUM_VALID_ENT_OPCODES + NUM_ENBISI_SPECIAL_STRING_IDS, ENBISI_neg_infinity, ENBISI_zero, ENBISI_one, diff --git a/src/Amalgam/Parser.cpp b/src/Amalgam/Parser.cpp index a12c1736..17fcda27 100644 --- a/src/Amalgam/Parser.cpp +++ b/src/Amalgam/Parser.cpp @@ -510,18 +510,10 @@ EvaluableNode *Parser::GetNextToken(EvaluableNode *parent_node, EvaluableNode *n //check for special values double value = 0.0; - if(s == ".nas") - { - new_token->SetType(ENT_STRING, evaluableNodeManager, false); - new_token->SetStringID(StringInternPool::NOT_A_STRING_ID); - return new_token; - } if(s == ".infinity") value = std::numeric_limits::infinity(); else if(s == "-.infinity") value = -std::numeric_limits::infinity(); - else if(s == ".nan") - value = std::numeric_limits::quiet_NaN(); else { auto [converted_value, success] = Platform_StringToNumber(s); @@ -598,6 +590,29 @@ EvaluableNode *Parser::ParseNextBlock() } else if(cur_node->IsAssociativeArray()) { + //if it's not an immediate value, then need to retrieve closing parenthesis + if(!IsEvaluableNodeTypeImmediate(n->GetType())) + { + SkipWhitespaceAndAccumulateAttributes(n); + if(pos <= code->size()) + { + auto cur_char = (*code)[pos]; + if(cur_char == ')') + { + pos++; + numOpenParenthesis--; + } + else + { + std::cerr << "Warning: " << "Missing ) at line " << lineNumber + 1 << " of " << originalSource << std::endl; + } + } + else //no more code + { + std::cerr << "Warning: " << "Mismatched ) at line " << lineNumber + 1 << " of " << originalSource << std::endl; + } + } + //n is the id, so need to get the next token StringInternPool::StringID index_sid = EvaluableNode::ToStringIDTakingReferenceAndClearing(n); @@ -863,7 +878,7 @@ void Parser::Unparse(UnparseData &upd, EvaluableNode *tree, EvaluableNode *paren auto sid = tree->GetStringIDReference(); if(sid == string_intern_pool.NOT_A_STRING_ID) { - upd.result.append(".nas"); + upd.result.append("(null)"); } else //legitimate string { diff --git a/src/Amalgam/SBFDSColumnData.h b/src/Amalgam/SBFDSColumnData.h index e4117c0a..77614a8e 100644 --- a/src/Amalgam/SBFDSColumnData.h +++ b/src/Amalgam/SBFDSColumnData.h @@ -23,7 +23,7 @@ class SBFDSColumnData //indicates the column does not use indices static constexpr size_t NO_INDEX = std::numeric_limits::max(); //nan value is always the 0th index - static constexpr size_t NAN_INDEX = 0; + static constexpr size_t NULL_INDEX = 0; //if empty, initialize to invalid index ValueEntry() @@ -74,10 +74,7 @@ class SBFDSColumnData else if(value_type == ENIVT_NUMBER) { numberIndices.insert(index); - if(FastIsNaN(value.number)) - nanIndices.insert(index); - else - entities_with_number_values.emplace_back(value.number, index); + entities_with_number_values.emplace_back(value.number, index); } else if(value_type == ENIVT_STRING_ID) { @@ -204,7 +201,7 @@ class SBFDSColumnData } if(internedNumberValues.valueInterningEnabled) - return EvaluableNodeImmediateValue(ValueEntry::NAN_INDEX); + return EvaluableNodeImmediateValue(ValueEntry::NULL_INDEX); else return EvaluableNodeImmediateValue(); } @@ -221,28 +218,9 @@ class SBFDSColumnData { double old_number_value = GetResolvedValue(old_value_type, old_value).number; double new_number_value = GetResolvedValue(new_value_type, new_value).number; - if(EqualIncludingNaN(old_number_value, new_number_value)) + if(old_number_value == new_number_value) return old_value; - //if made it here, then at least one of the values is not a NaN - //if one value is a NaN, just insert or delete as regular since there's little to be saved - if(FastIsNaN(old_number_value)) - { - nanIndices.erase(index); - return InsertIndexValue(new_value_type, new_value, index); - } - - if(FastIsNaN(new_number_value)) - { - DeleteIndexValue(old_value_type, old_value, index); - nanIndices.insert(index); - - if(internedNumberValues.valueInterningEnabled) - return EvaluableNodeImmediateValue(ValueEntry::NAN_INDEX); - else - return EvaluableNodeImmediateValue(std::numeric_limits::quiet_NaN()); - } - //if the value already exists, then put the index in the list //but return the lower bound if not found so don't have to search a second time //need to search the old value before inserting, as FindExactIndexForValue is fragile a placeholder empty entry @@ -453,25 +431,24 @@ class SBFDSColumnData case ENIVT_NUMBER: case ENIVT_NUMBER_INDIRECTION_INDEX: + { numberIndices.erase(index); - //remove, and if not a nan, then need to also remove the number - if(!nanIndices.EraseAndRetrieve(index)) - { - auto resolved_value = GetResolvedValue(value_type, value); + auto resolved_value = GetResolvedValue(value_type, value); - //look up value - auto [value_index, exact_index_found] = FindExactIndexForValue(resolved_value.number); - if(!exact_index_found) - return; + //look up value + auto [value_index, exact_index_found] = FindExactIndexForValue(resolved_value.number); + if(!exact_index_found) + return; + + //if the bucket has only one entry, we must delete the entire bucket + if(sortedNumberValueEntries[value_index]->indicesWithValue.size() == 1) + DeleteNumberValueEntry(value_index); + else //else we can just remove the id from the bucket + sortedNumberValueEntries[value_index]->indicesWithValue.erase(index); - //if the bucket has only one entry, we must delete the entire bucket - if(sortedNumberValueEntries[value_index]->indicesWithValue.size() == 1) - DeleteNumberValueEntry(value_index); - else //else we can just remove the id from the bucket - sortedNumberValueEntries[value_index]->indicesWithValue.erase(index); - } break; + } case ENIVT_STRING_ID: { @@ -541,7 +518,7 @@ class SBFDSColumnData invalidIndices.insert(index); if(internedNumberValues.valueInterningEnabled) - return EvaluableNodeImmediateValue(ValueEntry::NAN_INDEX); + return EvaluableNodeImmediateValue(ValueEntry::NULL_INDEX); else return value; } @@ -551,7 +528,7 @@ class SBFDSColumnData nullIndices.insert(index); if(internedNumberValues.valueInterningEnabled) - return EvaluableNodeImmediateValue(ValueEntry::NAN_INDEX); + return EvaluableNodeImmediateValue(ValueEntry::NULL_INDEX); else return value; } @@ -561,15 +538,6 @@ class SBFDSColumnData numberIndices.insert(index); double number_value = GetResolvedValue(value_type, value).number; - if(FastIsNaN(number_value)) - { - nanIndices.insert(index); - - if(internedNumberValues.valueInterningEnabled) - return EvaluableNodeImmediateValue(ValueEntry::NAN_INDEX); - else - return value; - } //if the value already exists, then put the index in the list //but return the lower bound if not found so don't have to search a second time @@ -784,8 +752,7 @@ class SBFDSColumnData } //given a feature_id and a range [low, high], inserts all the elements with values of feature feature_id within specified range into out; does not clear out - //Note about Null/NaNs: - //if the feature value is Nan/Null, it will NOT be present in the search results, ie "x" != 3 will NOT include elements with x is nan/Null, even though nan/null != 3 + //if the feature value is null, it will NOT be present in the search results, ie "x" != 3 will NOT include elements with x is null, even though null != 3 void FindAllIndicesWithinRange(EvaluableNodeImmediateValueType value_type, EvaluableNodeImmediateValue &low, EvaluableNodeImmediateValue &high, BitArrayIntegerSet &out, bool between_values = true) { @@ -801,26 +768,9 @@ class SBFDSColumnData if(FastIsNaN(low_number) || FastIsNaN(high_number)) { - //both are NaN + //both are NaN, return nothing if(FastIsNaN(low_number) && FastIsNaN(high_number)) - { - //if looking for NaN - if(between_values) - { - nanIndices.CopyTo(out); - } - else //looking for anything but NaN - { - numberIndices.CopyTo(out); - nanIndices.EraseTo(out); - } - return; - } - - //if NaN specified and within range, then we want to include NaN indices - if(between_values) - nanIndices.CopyTo(out); //modify range to include elements from or up to -/+inf if(FastIsNaN(low_number)) //find all NaN values and all values up to max @@ -839,10 +789,7 @@ class SBFDSColumnData if(between_values) return; else //the value doesn't exist, include everything - { - //include nans numberIndices.CopyTo(out); - } } //if within range, and range has no length, just return indices in that one bucket @@ -853,9 +800,6 @@ class SBFDSColumnData } else //if not within, populate with all indices not equal to value { - //include nans - nanIndices.CopyTo(out); - for(auto &value_entry : sortedNumberValueEntries) { if(value_entry->value.number == low_number) @@ -942,13 +886,6 @@ class SBFDSColumnData } else if(value_type == ENIVT_NUMBER) { - if(FastIsNaN(value.number)) - { - //only want nans - nanIndices.UnionTo(out); - return; - } - auto [value_index, exact_index_found] = FindExactIndexForValue(value.number); if(exact_index_found) out.InsertInBatch(sortedNumberValueEntries[value_index]->indicesWithValue); @@ -1135,10 +1072,6 @@ class SBFDSColumnData //indices of entities with a null for this feature EfficientIntegerSet nullIndices; - //indices of entities with a NaN for this feature - // the entities will also be included in numberIndices - EfficientIntegerSet nanIndices; - //indices that don't fall into the number/string/null types but are valid EfficientIntegerSet codeIndices; @@ -1215,11 +1148,11 @@ class SBFDSColumnData else //not valid, clear queue { unusedValueIndices.clear(); - //just use a new value, 0-based but leaving a spot open for NAN_INDEX + //just use a new value, 0-based but leaving a spot open for NULL_INDEX value_entry->valueInternIndex = total_num_values; } } - else //just use new value of the latest size, 0-based but leaving a spot open for NAN_INDEX + else //just use new value of the latest size, 0-based but leaving a spot open for NULL_INDEX { value_entry->valueInternIndex = total_num_values; } @@ -1272,9 +1205,6 @@ class SBFDSColumnData return ValueType(); }(); - //static constexpr ValueType notAValue = std::conditional_t, - // std::numeric_limits::quiet_NaN(), ValueType()>(); - //if valueInterningEnabled is true, then contains each value for the given index //if a given index isn't used, then it will contain the maximum value for the index //the 0th index is reserved for values that are not of type ValueType, diff --git a/src/Amalgam/SeparableBoxFilterDataStore.cpp b/src/Amalgam/SeparableBoxFilterDataStore.cpp index 45fbfe07..2fc5a7fa 100644 --- a/src/Amalgam/SeparableBoxFilterDataStore.cpp +++ b/src/Amalgam/SeparableBoxFilterDataStore.cpp @@ -58,9 +58,6 @@ void SeparableBoxFilterDataStore::OptimizeColumn(size_t column_index) GetValue(entity_index, column_index).number = value; } - for(auto entity_index : column_data->nanIndices) - GetValue(entity_index, column_index).number = std::numeric_limits::quiet_NaN(); - for(auto entity_index : column_data->nullIndices) GetValue(entity_index, column_index).number = std::numeric_limits::quiet_NaN(); @@ -78,11 +75,8 @@ void SeparableBoxFilterDataStore::OptimizeColumn(size_t column_index) GetValue(entity_index, column_index).indirectionIndex = value_index; } - for(auto entity_index : column_data->nanIndices) - GetValue(entity_index, column_index).number = SBFDSColumnData::ValueEntry::NAN_INDEX; - for(auto entity_index : column_data->nullIndices) - GetValue(entity_index, column_index).number = SBFDSColumnData::ValueEntry::NAN_INDEX; + GetValue(entity_index, column_index).number = SBFDSColumnData::ValueEntry::NULL_INDEX; } } @@ -334,17 +328,16 @@ void SeparableBoxFilterDataStore::FindEntitiesWithinDistance(GeneralizedDistance auto &column_data = columnData[absolute_feature_index]; auto &target_value = r_dist_eval.featureData[query_feature_index].targetValue; - if(target_value.IsNullEquivalent()) + if(target_value.IsNull()) { //add the appropriate unknown distance to each element double unknown_unknown_term = dist_eval.ComputeDistanceTermUnknownToUnknown(query_feature_index, high_accuracy); double known_unknown_term = dist_eval.ComputeDistanceTermKnownToUnknown(query_feature_index, high_accuracy); auto &null_indices = column_data->nullIndices; - auto &nan_indices = column_data->nanIndices; for(auto entity_index : enabled_indices) { - if(null_indices.contains(entity_index) || nan_indices.contains(entity_index)) + if(null_indices.contains(entity_index)) distances[entity_index] += unknown_unknown_term; else distances[entity_index] += known_unknown_term; @@ -730,10 +723,10 @@ void SeparableBoxFilterDataStore::FindNearestEntities(GeneralizedDistanceEvaluat bool need_enabled_indices_recount = false; for(size_t i = 0; i < num_enabled_features; i++) { - //if the target_value is a null/nan, unknown-unknown differences have already been accounted for + //if the target_value is a null, unknown-unknown differences have already been accounted for //since they are partial matches auto &feature_data = r_dist_eval.featureData[i]; - if(feature_data.targetValue.IsNullEquivalent()) + if(feature_data.targetValue.IsNull()) continue; if(dist_eval.ComputeDistanceTermKnownToUnknown(i, high_accuracy) > worst_candidate_distance) @@ -747,14 +740,6 @@ void SeparableBoxFilterDataStore::FindNearestEntities(GeneralizedDistanceEvaluat null_indices.EraseInBatchFrom(enabled_indices); need_enabled_indices_recount = true; } - - auto &nan_indices = column->nanIndices; - //make sure there's enough nulls to justify running through all of enabled_indices - if(nan_indices.size() > 20) - { - nan_indices.EraseInBatchFrom(enabled_indices); - need_enabled_indices_recount = true; - } } } if(need_enabled_indices_recount) @@ -892,7 +877,7 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(R auto &value = feature_data.targetValue; //need to accumulate values for nulls if the value is a null - if(value.IsNullEquivalent()) + if(value.IsNull()) { double unknown_unknown_term = r_dist_eval.distEvaluator->ComputeDistanceTermUnknownToUnknown(query_feature_index, high_accuracy); @@ -909,25 +894,14 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(R if(unknown_unknown_term == known_unknown_term && unknown_unknown_term > 0) return unknown_unknown_term; - //find nas values -- common to both branches below - auto nas_iter = column->stringIdValueToIndices.find(string_intern_pool.NOT_A_STRING_ID); - if(unknown_unknown_term < known_unknown_term || known_unknown_term == 0.0) - { AccumulatePartialSums(column->nullIndices, query_feature_index, unknown_unknown_term); - AccumulatePartialSums(column->nanIndices, query_feature_index, unknown_unknown_term); - if(nas_iter != end(column->stringIdValueToIndices)) - AccumulatePartialSums(*nas_iter->second, query_feature_index, unknown_unknown_term); - } if(known_unknown_term < unknown_unknown_term || unknown_unknown_term == 0.0) { BitArrayIntegerSet &known_unknown_indices = parametersAndBuffers.potentialMatchesSet; known_unknown_indices = enabled_indices; column->nullIndices.EraseTo(known_unknown_indices); - column->nanIndices.EraseTo(known_unknown_indices); - if(nas_iter != end(column->stringIdValueToIndices)) - known_unknown_indices.erase(*nas_iter->second); AccumulatePartialSums(known_unknown_indices, query_feature_index, known_unknown_term); } @@ -945,10 +919,6 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(R else //nonsymmetric nominal -- need to compute { AccumulatePartialSums(column->nullIndices, query_feature_index, unknown_unknown_term); - AccumulatePartialSums(column->nanIndices, query_feature_index, unknown_unknown_term); - auto nas_iter = column->stringIdValueToIndices.find(string_intern_pool.NOT_A_STRING_ID); - if(nas_iter != end(column->stringIdValueToIndices)) - AccumulatePartialSums(*nas_iter->second, query_feature_index, unknown_unknown_term); double nonmatch_dist_term = r_dist_eval.ComputeDistanceTermNominalNonNullSmallestNonmatch(query_feature_index, high_accuracy); //if the next closest match is larger, no need to compute any more values @@ -982,10 +952,6 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(R { double known_unknown_term = r_dist_eval.distEvaluator->ComputeDistanceTermKnownToUnknown(query_feature_index, high_accuracy); AccumulatePartialSums(column->nullIndices, query_feature_index, known_unknown_term); - AccumulatePartialSums(column->nanIndices, query_feature_index, known_unknown_term); - auto nas_iter = column->stringIdValueToIndices.find(string_intern_pool.NOT_A_STRING_ID); - if(nas_iter != end(column->stringIdValueToIndices)) - AccumulatePartialSums(*nas_iter->second, query_feature_index, known_unknown_term); } //if nominal, only need to compute the exact match diff --git a/src/Amalgam/SeparableBoxFilterDataStore.h b/src/Amalgam/SeparableBoxFilterDataStore.h index 320a165f..382e8507 100644 --- a/src/Amalgam/SeparableBoxFilterDataStore.h +++ b/src/Amalgam/SeparableBoxFilterDataStore.h @@ -218,8 +218,7 @@ class SeparableBoxFilterDataStore } //given a feature_id and a range [low, high], fills out with all the entities with values of feature feature_id within specified range - //Note about Null/NaNs: - //if the feature value is Nan/Null, it will NOT be present in the search results, ie "x" != 3 will NOT include elements with x is nan/Null, even though nan/null != 3 + //if the feature value is null, it will NOT be present in the search results, ie "x" != 3 will NOT include elements with x is null, even though null != 3 inline void FindAllEntitiesWithinRange(size_t feature_id, EvaluableNodeImmediateValueType value_type, EvaluableNodeImmediateValue &low, EvaluableNodeImmediateValue &high, BitArrayIntegerSet &out, bool between_values = true) { @@ -294,7 +293,6 @@ class SeparableBoxFilterDataStore auto &column_data = columnData[column_index]; column_data->numberIndices.CopyTo(enabled_entities); - column_data->nanIndices.EraseTo(enabled_entities); //resize buffers and place each entity and value into its respective buffer entities.resize(enabled_entities.size()); @@ -324,7 +322,6 @@ class SeparableBoxFilterDataStore auto &column_data = columnData[column_index]; column_data->numberIndices.IntersectTo(enabled_entities); - column_data->nanIndices.EraseTo(enabled_entities); //resize buffers and place each entity and value into its respective buffer entities.resize(enabled_entities.size()); @@ -519,7 +516,7 @@ class SeparableBoxFilterDataStore //deletes the index and associated data void DeleteEntityIndexFromColumns(size_t entity_index); - //adds a new labels to the database, populating new cells with -NaN, and updating the number of entities + //adds a new labels to the database // assumes label_ids is not empty and num_entities is nonzero //returns the number of new columns inserted size_t AddLabelsAsEmptyColumns(std::vector &label_ids, size_t num_entities); diff --git a/src/Amalgam/amlg_code/full_test.amlg b/src/Amalgam/amlg_code/full_test.amlg index 2575bdeb..403b7b31 100644 --- a/src/Amalgam/amlg_code/full_test.amlg +++ b/src/Amalgam/amlg_code/full_test.amlg @@ -24,7 +24,7 @@ (print "--parse and unparse--\n") (print (unparse (parse "(print \"hello\")")) "\n") - (print (parse (unparse (list (sqrt -1) .nan .infinity -.infinity))) "\n") + (print (parse (unparse (list (sqrt -1) (null) .infinity -.infinity))) "\n") (print (unparse (associate "a" 1 "b" 2 "c" (list "alpha" "beta" "gamma"))) "\n") (print (unparse (associate "a" 1 "b" 2 "c" (list "alpha" "beta" "gamma")) (true)) "\n") @@ -167,7 +167,7 @@ (print accum_assoc "\n") (assign "x" 1) - (accum "x" (null) 4) + (accum "x" (list) 4) (print x "\n") (assign "x" (list 0 1 2 (associate "a" 1 "b" 2 "c" 3))) @@ -381,10 +381,10 @@ (print "10 " (generalized_distance (list 0.5 0.5) (null) (null) (null) 0.01 (list 3 4) ) "\n") (print "11 " (generalized_distance (list 0.5 0.5) (null) (null) (null) 0.001 (list 3 4) ) "\n") (print "12 " (generalized_distance (list 0.5 0.5) (null) (null) (null) 0 (list 3 4) ) "\n") - (print "13 " (generalized_distance (list 1 1) (null) (null) (null) 2 (list .nan 4) ) "\n") - (print "14 " (generalized_distance (list 1 1) (null) (null) (null) 0 (list .nan 4) ) "\n") - (print "15 " (generalized_distance (list 0.5 0.5) (null) (null) (null) 2 (list .nan 4) ) "\n") - (print "16 " (generalized_distance (list 0.5 0.5) (null) (null) (null) 0 (list .nan 4) ) "\n") + (print "13 " (generalized_distance (list 1 1) (null) (null) (null) 2 (list (null) 4) ) "\n") + (print "14 " (generalized_distance (list 1 1) (null) (null) (null) 0 (list (null) 4) ) "\n") + (print "15 " (generalized_distance (list 0.5 0.5) (null) (null) (null) 2 (list (null) 4) ) "\n") + (print "16 " (generalized_distance (list 0.5 0.5) (null) (null) (null) 0 (list (null) 4) ) "\n") (print "17 " (generalized_distance (null) (list "nominal_numeric") (list 1) (null) 1 (list 1 2 3) (list 10 2 4) ) "\n") (print "18 " (generalized_distance (null) (list "nominal_numeric") (list 1) (null) 1 (list 1 2 3) (list 10 2 10) ) "\n") (print "19 " (generalized_distance (null) (list "nominal_numeric") (list 1) (null) 1 (list 1 2 3) (list 10 2 10) ) "\n") @@ -759,7 +759,7 @@ (print (filter (lambda (< (current_index) 3)) (list 10 1 20 2 30 3 40 4))) (print (filter (lambda (< (current_index) 20)) (associate 10 1 20 2 30 3 40 4))) - (print (filter (list 10 1 20 (null) 30 .nan .nas 40 4))) + (print (filter (list 10 1 20 (null) 30 (null) (null) 40 4))) (print (filter (list 10 1 20 (null) 30 "" 40 4))) (print (filter (assoc a 10 b 1 c 20 d "" e 30 f 3 g (null) h 4))) (print (filter (assoc a 10 b 1 c 20 d "" e 30 f 3 g (null) h 4))) @@ -971,7 +971,7 @@ (print "(null): " (get get_test_assoc (list "A" "C")) "\n") (print "(null): " (get get_test_assoc (list "B" "C")) "\n") - (print (get (assoc .nas 3) .nas) "\n") + (print (get (assoc (null) 3) (null)) "\n") (print "--set--\n") (print (set (associate "a" 1 "b" 2 "c" 3 4 "d") "e" 5)) @@ -1030,8 +1030,8 @@ (print "--=--\n") (print (= 4 4 5) "\n") (print (= 4 4 4) "\n") - (print (= (sqrt -1) .nan) "\n") - (print (= .nan .nan) "\n") + (print (= (sqrt -1) (null)) "\n") + (print (= (null) (null)) "\n") (print (= .infinity .infinity) "\n") (print (= .infinity -.infinity) "\n") @@ -1056,7 +1056,7 @@ (print (<= 4 5 6) "\n") (print (<= 4 5 6 5) "\n") (print (<= (null) 2) "\n") - (print (<= 2 .nan) "\n") + (print (<= 2 (null)) "\n") (print "-->--\n") (print (> 6 5) "\n") @@ -1070,7 +1070,7 @@ (print (>= 6 5 4) "\n") (print (>= 6 5 4 5) "\n") (print (>= (null) 2) "\n") - (print (>= 2 .nan) "\n") + (print (>= 2 (null)) "\n") (print "--~--\n") (print (~ 1 4 5) "\n") @@ -1158,9 +1158,6 @@ (print .infinity "\n") (print (- (* 3 .infinity)) "\n") - (print "--nan--\n") - (print .nan "\n") - (print "--list--\n") (print (list "a" 1 "b")) @@ -1224,8 +1221,8 @@ (print "20: " (format "[{\"a\" : 3, \"b\" : 4}, {\"c\" : \"c\"}]" "json" "code")) (print "21: " (format (list (assoc a 3 b 4) (assoc c "c" d (null))) "code" "json") "\n") (print "22: " (format (list (assoc a 3 b 4) (assoc c "c" d (null))) "code" "json" (null) (assoc sort_keys (true)) ) "\n") - (print "23: " (format (assoc "a" 1 "b" 2 "c" 3 "d" 4 "e" (list "a" "b" .nan .infinity)) "code" "yaml") "\n") - (print "24: " (format (assoc "a" 1 "b" 2 "c" 3 "d" 4 "e" (list "a" "b" .nan .infinity)) "code" "yaml" (null) (assoc "sort_keys" (true))) "\n") + (print "23: " (format (assoc "a" 1 "b" 2 "c" 3 "d" 4 "e" (list "a" "b" (null) .infinity)) "code" "yaml") "\n") + (print "24: " (format (assoc "a" 1 "b" 2 "c" 3 "d" 4 "e" (list "a" "b" (null) .infinity)) "code" "yaml" (null) (assoc "sort_keys" (true))) "\n") (print "25: " (format "a: 1" "yaml" "code")) ;current date @@ -1443,7 +1440,7 @@ (print "--print--\n") (print (list 0 1 10 12 100 120 122 1000 1000.123 10000 100000 .1 .01 .001 .0001 .00001 .0000123456789 1.2345e-149) ) (print (list -0 -1 -10 -12 -100 -120 -122 -1000 -1000.123 -10000 -100000 -.1 -.01 -.001 -.0001 -.00001 -.0000123456789 -1.2345e-149) ) - (print .nan "\n" .infinity "\n" (true) "\n" (false) "\n") + (print .infinity "\n" (true) "\n" (false) "\n") (print "--total_size--\n") (print (total_size (list 1 2 3 (associate "a" 3 "b" 4) (list 5 6))) "\n") @@ -1476,7 +1473,7 @@ ) "\n") (print (commonality .infinity 3) "\n") - (print (commonality .nan 3) "\n") + (print (commonality (null) 3) "\n") (print (commonality .infinity .infinity) "\n") (print (commonality .infinity -.infinity) "\n") (print (commonality "hello" "hello") "\n") @@ -3341,7 +3338,7 @@ (print "--assign_to_entities--\n") - #a2e .nan + #a2e (null) (assign_to_entities (assoc a2e (null))) (print a2e) @@ -3441,10 +3438,10 @@ ) ) - (print "--nan equality tests--\n") + (print "--null equality tests--\n") - (print "(= (null) .nan): " (= (null) .nan) "\n") - (print "(= (+ (null)) .nan): " (= (+ (null)) .nan) "\n") + (print "(= (null) (null)): " (= (null) (null)) "\n") + (print "(= (+ (null)) (null)): " (= (+ (null)) (null)) "\n") (create_entities (list "NaNTest") (null)) @@ -3469,7 +3466,7 @@ (create_entities (list "NaNTest" "EntityNaN") (lambda (null - ##label1 .nan + ##label1 (null) ##label2 1 ) ) @@ -3482,7 +3479,7 @@ ) (print (contained_entities "NaNTest" (list - (query_equals "label1" .nan) + (query_equals "label1" (null)) (query_exists "label2") )) ) @@ -4123,7 +4120,7 @@ (let (assoc x (list)) ||(map (lambda (let (assoc y (current_value 1) ) - (accum "x" (null) y) + (accum "x" (list) y) ) ) (range 1 1000) diff --git a/src/Amalgam/entity/EntityQueryBuilder.h b/src/Amalgam/entity/EntityQueryBuilder.h index b90a488e..09e02e60 100644 --- a/src/Amalgam/entity/EntityQueryBuilder.h +++ b/src/Amalgam/entity/EntityQueryBuilder.h @@ -77,7 +77,7 @@ namespace EntityQueryBuilder inline void PopulateFeatureDeviationNominalValueData( NominalDeviationValuesType &ndd, EvaluableNode *value_deviation_node) { - if(EvaluableNode::IsEmptyNode(value_deviation_node)) + if(EvaluableNode::IsNull(value_deviation_node)) return; auto vdn_type = value_deviation_node->GetType(); @@ -95,7 +95,7 @@ namespace EntityQueryBuilder size_t ocn_size = ocn.size(); if(ocn_size > 0 - && !EvaluableNode::IsEmptyNode(ocn[0]) + && !EvaluableNode::IsNull(ocn[0]) && ocn[0]->GetType() == ENT_ASSOC) PopulateFeatureDeviationNominalValueAssocData(ndd, ocn[0]); @@ -472,7 +472,7 @@ namespace EntityQueryBuilder cur_condition->weightLabel = EvaluableNode::ToStringIDIfExists(ocn[ENTITY_WEIGHT_LABEL_NAME]); //set random seed - cur_condition->hasRandomStream = (ocn.size() > RANDOM_SEED && !EvaluableNode::IsEmptyNode(ocn[RANDOM_SEED])); + cur_condition->hasRandomStream = (ocn.size() > RANDOM_SEED && !EvaluableNode::IsNull(ocn[RANDOM_SEED])); if(cur_condition->hasRandomStream) cur_condition->randomStream.SetState(EvaluableNode::ToStringPreservingOpcodeType(ocn[RANDOM_SEED])); else @@ -511,7 +511,7 @@ namespace EntityQueryBuilder { EvaluableNode *list_param = ocn[NUM_MINKOWSKI_DISTANCE_QUERY_PARAMETERS + 0]; cur_condition->returnSortedList = EvaluableNode::IsTrue(list_param); - if(!EvaluableNode::IsEmptyNode(list_param)) + if(!EvaluableNode::IsNull(list_param)) { if(list_param->GetType() == ENT_STRING) { @@ -537,7 +537,7 @@ namespace EntityQueryBuilder { EvaluableNode *list_param = ocn[NUM_MINKOWSKI_DISTANCE_QUERY_PARAMETERS + 1]; cur_condition->returnSortedList = EvaluableNode::IsTrue(list_param); - if(!EvaluableNode::IsEmptyNode(list_param)) + if(!EvaluableNode::IsNull(list_param)) { if(list_param->GetType() == ENT_STRING) { @@ -669,7 +669,7 @@ namespace EntityQueryBuilder cur_condition->hasStartOffset = (ocn.size() >= 2); cur_condition->startOffset = cur_condition->hasStartOffset ? static_cast(EvaluableNode::ToNumber(ocn[1], 0.0)) : 0; - cur_condition->hasRandomStream = (ocn.size() >= 3 && !EvaluableNode::IsEmptyNode(ocn[2])); + cur_condition->hasRandomStream = (ocn.size() >= 3 && !EvaluableNode::IsNull(ocn[2])); if(cur_condition->hasRandomStream) cur_condition->randomStream.SetState(EvaluableNode::ToStringPreservingOpcodeType(ocn[2])); else @@ -680,7 +680,7 @@ namespace EntityQueryBuilder case ENT_QUERY_SAMPLE: { cur_condition->maxToRetrieve = (ocn.size() > 0) ? EvaluableNode::ToNumber(ocn[0], 0.0) : 1; - cur_condition->hasRandomStream = (ocn.size() > 1 && !EvaluableNode::IsEmptyNode(ocn[1])); + cur_condition->hasRandomStream = (ocn.size() > 1 && !EvaluableNode::IsNull(ocn[1])); if(cur_condition->hasRandomStream) cur_condition->randomStream.SetState(EvaluableNode::ToStringPreservingOpcodeType(ocn[1])); else @@ -691,7 +691,7 @@ namespace EntityQueryBuilder { cur_condition->singleLabel = (ocn.size() > 0) ? EvaluableNode::ToStringIDIfExists(ocn[0]) : StringInternPool::NOT_A_STRING_ID; cur_condition->maxToRetrieve = (ocn.size() > 1) ? EvaluableNode::ToNumber(ocn[1], 0.0) : 1; - cur_condition->hasRandomStream = (ocn.size() > 2 && !EvaluableNode::IsEmptyNode(ocn[2])); + cur_condition->hasRandomStream = (ocn.size() > 2 && !EvaluableNode::IsNull(ocn[2])); if(cur_condition->hasRandomStream) cur_condition->randomStream.SetState(EvaluableNode::ToStringPreservingOpcodeType(ocn[2])); else @@ -724,7 +724,7 @@ namespace EntityQueryBuilder EvaluableNode *high_value = ocn[2]; //since types need to match, force both to the same type - if(EvaluableNode::IsNativelyNumeric(low_value) || EvaluableNode::IsNativelyNumeric(high_value)) + if(EvaluableNode::IsNumericOrNull(low_value) || EvaluableNode::IsNumericOrNull(high_value)) { cur_condition->pairedLabels.push_back(std::make_pair(label_sid, std::make_pair( EvaluableNode::ToNumber(low_value), EvaluableNode::ToNumber(high_value)))); @@ -801,7 +801,7 @@ namespace EntityQueryBuilder //number of parameters checked above EvaluableNode *compare_value = ocn[1]; - if(EvaluableNode::IsNativelyNumeric(compare_value)) + if(EvaluableNode::IsNumericOrNull(compare_value)) { if(type == ENT_QUERY_LESS_OR_EQUAL_TO) cur_condition->pairedLabels.push_back(std::make_pair(label_sid, std::make_pair( diff --git a/src/Amalgam/evaluablenode/EvaluableNode.cpp b/src/Amalgam/evaluablenode/EvaluableNode.cpp index c4828b7c..358fc114 100644 --- a/src/Amalgam/evaluablenode/EvaluableNode.cpp +++ b/src/Amalgam/evaluablenode/EvaluableNode.cpp @@ -26,7 +26,7 @@ void EvaluableNode::GetNodeCommonAndUniqueLabelCounts(EvaluableNode *n1, Evaluab //if no labels in either, then done if(num_n1_labels == 0 && num_n2_labels == 0) return; - + //if labels in one (but not both, because would have exited), then count total and done if(num_n1_labels == 0 || num_n2_labels == 0) { @@ -84,9 +84,9 @@ bool EvaluableNode::AreShallowEqual(EvaluableNode *a, EvaluableNode *b) { double av = EvaluableNode::ToNumber(a); double bv = EvaluableNode::ToNumber(b); - return EqualIncludingNaN(av, bv); + return (av == bv); } - + //if made it here, then it's an instruction, and they're of equal type return true; } @@ -103,14 +103,12 @@ bool EvaluableNode::IsTrue(EvaluableNode *n) return false; if(node_type == ENT_NULL) return false; - + if(DoesEvaluableNodeTypeUseNumberData(node_type)) { double &num = n->GetNumberValueReference(); if(num == 0.0) return false; - if(FastIsNaN(num)) - return false; return true; } @@ -148,7 +146,7 @@ int EvaluableNode::Compare(EvaluableNode *a, EvaluableNode *b) else return 0; } - + //compare via strings //first check if they're the same if(a != nullptr && b != nullptr) @@ -216,8 +214,8 @@ const std::string EvaluableNode::ToStringPreservingOpcodeType(EvaluableNode *e) std::pair EvaluableNode::ToString(EvaluableNode *e) { - if(IsEmptyNode(e)) - return std::make_pair(false, ".nas"); + if(IsNull(e)) + return std::make_pair(false, "(null)"); switch(e->GetType()) { @@ -237,7 +235,7 @@ std::pair EvaluableNode::ToString(EvaluableNode *e) StringInternPool::StringID EvaluableNode::ToStringIDIfExists(EvaluableNode *e) { - if(EvaluableNode::IsEmptyNode(e)) + if(EvaluableNode::IsNull(e)) return StringInternPool::NOT_A_STRING_ID; if((e->GetType() == ENT_STRING || e->GetType() == ENT_SYMBOL)) @@ -251,20 +249,20 @@ StringInternPool::StringID EvaluableNode::ToStringIDIfExists(EvaluableNode *e) StringInternPool::StringID EvaluableNode::ToStringIDWithReference(EvaluableNode *e) { - if(EvaluableNode::IsEmptyNode(e)) + if(EvaluableNode::IsNull(e)) return StringInternPool::NOT_A_STRING_ID; if(e->GetType() == ENT_STRING || e->GetType() == ENT_SYMBOL) return string_intern_pool.CreateStringReference(e->GetStringIDReference()); - + std::string stringified = ToStringPreservingOpcodeType(e); return string_intern_pool.CreateStringReference(stringified); } StringInternPool::StringID EvaluableNode::ToStringIDTakingReferenceAndClearing(EvaluableNode *e) { - //NaS doesn't need a reference - if(IsEmptyNode(e)) + //null doesn't need a reference + if(IsNull(e)) return StringInternPool::NOT_A_STRING_ID; if(e->GetType() == ENT_STRING || e->GetType() == ENT_SYMBOL) @@ -294,7 +292,7 @@ void EvaluableNode::ConvertOrderedListToNumberedAssoc() //convert ordered child nodes into index number -> value auto &ocn = GetOrderedChildNodes(); - new_map.reserve(ocn.size()); + new_map.reserve(ocn.size()); for(size_t i = 0; i < ocn.size(); i++) new_map[string_intern_pool.CreateStringReference(NumberToString(i))] = ocn[i]; @@ -503,7 +501,7 @@ void EvaluableNode::SetType(EvaluableNodeType new_type, EvaluableNodeManager *en if( (DoesEvaluableNodeTypeUseNumberData(cur_type) && DoesEvaluableNodeTypeUseNumberData(new_type)) || (DoesEvaluableNodeTypeUseStringData(cur_type) && DoesEvaluableNodeTypeUseStringData(new_type)) - || (DoesEvaluableNodeTypeUseAssocData(cur_type) && DoesEvaluableNodeTypeUseAssocData(new_type)) + || (DoesEvaluableNodeTypeUseAssocData(cur_type) && DoesEvaluableNodeTypeUseAssocData(new_type)) || (DoesEvaluableNodeTypeUseOrderedData(cur_type) && DoesEvaluableNodeTypeUseOrderedData(new_type)) ) { type = new_type; @@ -606,7 +604,7 @@ void EvaluableNode::SetType(EvaluableNodeType new_type, EvaluableNodeManager *en InitOrderedChildNodes(); } } - + type = new_type; //cleared child nodes, so no cycles @@ -659,24 +657,31 @@ void EvaluableNode::InitStringValue() void EvaluableNode::SetStringID(StringInternPool::StringID id) { - if(DoesEvaluableNodeTypeUseStringData(GetType())) + if(id == StringInternPool::NOT_A_STRING_ID) { - if(!HasExtendedValue()) + SetType(ENT_NULL); + } + else + { + if(DoesEvaluableNodeTypeUseStringData(GetType())) { - StringInternPool::StringID cur_id = value.stringValueContainer.stringID; - if(id != cur_id) + if(!HasExtendedValue()) { - string_intern_pool.DestroyStringReference(cur_id); - value.stringValueContainer.stringID = string_intern_pool.CreateStringReference(id); + StringInternPool::StringID cur_id = value.stringValueContainer.stringID; + if(id != cur_id) + { + string_intern_pool.DestroyStringReference(cur_id); + value.stringValueContainer.stringID = string_intern_pool.CreateStringReference(id); + } } - } - else - { - StringInternPool::StringID cur_id = value.extension.extendedValue->value.stringValueContainer.stringID; - if(id != cur_id) + else { - string_intern_pool.DestroyStringReference(cur_id); - value.extension.extendedValue->value.stringValueContainer.stringID = string_intern_pool.CreateStringReference(id); + StringInternPool::StringID cur_id = value.extension.extendedValue->value.stringValueContainer.stringID; + if(id != cur_id) + { + string_intern_pool.DestroyStringReference(cur_id); + value.extension.extendedValue->value.stringValueContainer.stringID = string_intern_pool.CreateStringReference(id); + } } } } @@ -744,19 +749,26 @@ StringInternPool::StringID EvaluableNode::GetAndClearStringIDWithReference() void EvaluableNode::SetStringIDWithReferenceHandoff(StringInternPool::StringID id) { - if(DoesEvaluableNodeTypeUseStringData(GetType())) + if(id == StringInternPool::NOT_A_STRING_ID) { - if(!HasExtendedValue()) - { - StringInternPool::StringID cur_id = value.stringValueContainer.stringID; - string_intern_pool.DestroyStringReference(cur_id); - value.stringValueContainer.stringID = id; - } - else + SetType(ENT_NULL); + } + else + { + if(DoesEvaluableNodeTypeUseStringData(GetType())) { - StringInternPool::StringID cur_id = value.extension.extendedValue->value.stringValueContainer.stringID; - string_intern_pool.DestroyStringReference(cur_id); - value.extension.extendedValue->value.stringValueContainer.stringID = id; + if(!HasExtendedValue()) + { + StringInternPool::StringID cur_id = value.stringValueContainer.stringID; + string_intern_pool.DestroyStringReference(cur_id); + value.stringValueContainer.stringID = id; + } + else + { + StringInternPool::StringID cur_id = value.extension.extendedValue->value.stringValueContainer.stringID; + string_intern_pool.DestroyStringReference(cur_id); + value.extension.extendedValue->value.stringValueContainer.stringID = id; + } } } } @@ -1013,7 +1025,7 @@ std::vector EvaluableNode::GetCommentsSeparateLines() //early exit if(full_comments.empty()) return comment_lines; - + size_t cur = 0; size_t prev = 0; while((cur = full_comments.find('\n', prev)) != std::string::npos) @@ -1129,7 +1141,7 @@ size_t EvaluableNode::GetNumChildNodes() return GetMappedChildNodesReference().size(); else return GetOrderedChildNodesReference().size(); - + return 0; } @@ -1199,7 +1211,7 @@ void EvaluableNode::AppendOrderedChildNode(EvaluableNode *cn) return; GetOrderedChildNodesReference().push_back(cn); - + if(cn != nullptr) { //if cycles, propagate upward @@ -1623,7 +1635,7 @@ void EvaluableNode::Invalidate() value.numberValueContainer.labelStringID = StringInternPool::NOT_A_STRING_ID; return; } - + //has extended type switch(GetType()) { @@ -1655,7 +1667,7 @@ void EvaluableNode::Invalidate() } bool EvaluableNode::AreDeepEqualGivenShallowEqual(EvaluableNode *a, EvaluableNode *b, ReferenceAssocType *checked) -{ +{ //if either is a null and have same number of child nodes, then equal if(a == nullptr || b == nullptr) return true; @@ -1689,7 +1701,7 @@ bool EvaluableNode::AreDeepEqualGivenShallowEqual(EvaluableNode *a, EvaluableNod size_t a_size = a_mcn.size(); if(a_size != b_mcn.size()) return false; - + //both empty, so equal if(a_size == 0) return true; @@ -1716,7 +1728,7 @@ bool EvaluableNode::AreDeepEqualGivenShallowEqual(EvaluableNode *a, EvaluableNod if(!EvaluableNode::AreDeepEqualGivenShallowEqual(a_child, b_child, checked)) return false; } - + //all child nodes are equal return true; } @@ -1770,7 +1782,7 @@ bool EvaluableNode::CanNodeTreeBeFlattenedRecurse(EvaluableNode *n, std::vector< { if(e == nullptr) continue; - + if(!CanNodeTreeBeFlattenedRecurse(e, stack)) return false; } diff --git a/src/Amalgam/evaluablenode/EvaluableNode.h b/src/Amalgam/evaluablenode/EvaluableNode.h index aa5410f7..bf86eda7 100644 --- a/src/Amalgam/evaluablenode/EvaluableNode.h +++ b/src/Amalgam/evaluablenode/EvaluableNode.h @@ -73,28 +73,52 @@ class EvaluableNode inline void InitializeType(EvaluableNodeType _type, StringInternPool::StringID string_id) { - type = _type; attributes.allAttributes = 0; - value.stringValueContainer.stringID = string_intern_pool.CreateStringReference(string_id); - value.stringValueContainer.labelStringID = StringInternPool::NOT_A_STRING_ID; + if(string_id == StringInternPool::NOT_A_STRING_ID) + { + type = ENT_NULL; + value.ConstructOrderedChildNodes(); + } + else + { + type = _type; + value.stringValueContainer.stringID = string_intern_pool.CreateStringReference(string_id); + value.stringValueContainer.labelStringID = StringInternPool::NOT_A_STRING_ID; + } } //like InitializeType, but hands off the string reference to string_id inline void InitializeTypeWithReferenceHandoff(EvaluableNodeType _type, StringInternPool::StringID string_id) { - type = _type; attributes.allAttributes = 0; - value.stringValueContainer.stringID = string_id; - value.stringValueContainer.labelStringID = StringInternPool::NOT_A_STRING_ID; + if(string_id == StringInternPool::NOT_A_STRING_ID) + { + type = ENT_NULL; + value.ConstructOrderedChildNodes(); + } + else + { + type = _type; + value.stringValueContainer.stringID = string_id; + value.stringValueContainer.labelStringID = StringInternPool::NOT_A_STRING_ID; + } } constexpr void InitializeType(double number_value) { - type = ENT_NUMBER; attributes.allAttributes = 0; - attributes.individualAttribs.isIdempotent = true; - value.numberValueContainer.labelStringID = StringInternPool::NOT_A_STRING_ID; - value.numberValueContainer.numberValue = number_value; + if(FastIsNaN(number_value)) + { + type = ENT_NULL; + value.ConstructOrderedChildNodes(); + } + else + { + type = ENT_NUMBER; + attributes.individualAttribs.isIdempotent = true; + value.numberValueContainer.labelStringID = StringInternPool::NOT_A_STRING_ID; + value.numberValueContainer.numberValue = number_value; + } } //initializes to ENT_UNINITIALIZED @@ -200,20 +224,6 @@ class EvaluableNode //Returns true if this node evaluates to true static bool IsTrue(EvaluableNode *n); - //returns true if it is explicitly a string - constexpr bool IsStringValue() - { - return (GetType() == ENT_STRING); - } - - //returns true if it is explicitly a string - static constexpr bool IsStringValue(EvaluableNode *n) - { - if(n == nullptr) - return false; - return n->IsStringValue(); - } - //Returns true if the node is some form of associative array constexpr bool IsAssociativeArray() { @@ -279,7 +289,7 @@ class EvaluableNode return !IsLessThan(a, b, true); } - //if the node's contents can be represented as a number, which includes numbers, infinity, and even null and NaN, then return true + //if the node's contents can be represented as a number, which includes numbers, infinity, then return true // otherwise returns false static constexpr bool CanRepresentValueAsANumber(EvaluableNode *e) { @@ -304,32 +314,12 @@ class EvaluableNode return (e == nullptr || e->GetType() == ENT_NULL); } - //returns true if node pointer e resolves to NaN (not a number) when interpreted as a number - static constexpr bool IsNaN(EvaluableNode *e) - { - return (IsNull(e) || FastIsNaN(e->GetNumberValue())); - } - - //returns true if node pointer e resolves to NaS (not a string) when interpreted as a string - static constexpr bool IsNaS(EvaluableNode *e) - { - return (IsNull(e) || e->GetStringID() == string_intern_pool.NOT_A_STRING_ID); - } - - //returns true if node pointer is nullptr, ENT_NULL, NaN number, or NaS string - static constexpr bool IsEmptyNode(EvaluableNode *e) - { - return (IsNull(e) - || (e->IsNativelyNumeric() && FastIsNaN(e->GetNumberValue())) - || (e->IsNativelyString() && e->GetStringID() == string_intern_pool.NOT_A_STRING_ID) ); - } - //Converts the node to a number //if null, then will return value_if_null static double ToNumber(EvaluableNode *e, double value_if_null = std::numeric_limits::quiet_NaN()); //returns true if the node can directly be interpreted as a number - static constexpr bool IsNativelyNumeric(EvaluableNode *e) + static constexpr bool IsNumericOrNull(EvaluableNode *e) { if(e == nullptr) return true; @@ -342,17 +332,11 @@ class EvaluableNode } //returns true if the EvaluableNode uses numeric data - constexpr bool IsNativelyNumeric() + constexpr bool IsNumericOrNull() { return DoesEvaluableNodeTypeUseNumberData(GetType()); } - //returns true if the EvaluableNode uses string data - constexpr bool IsNativelyString() - { - return DoesEvaluableNodeTypeUseStringData(GetType()); - } - //Converts a number to a string in a consistent way that should be used for anything dealing with EvaulableNode static __forceinline std::string NumberToString(double value) { @@ -367,10 +351,10 @@ class EvaluableNode //converts the node to a string that represents the opcode const static std::string ToStringPreservingOpcodeType(EvaluableNode *e); - //converts the node to a string, returning true if valid. If it doesn't exist, or any form of null/NaN/NaS, it returns false + //converts the node to a string, returning true if valid. If it doesn't exist or it's null, it returns false static std::pair ToString(EvaluableNode *e); - //converts node to an existing string. If it doesn't exist, or any form of null/NaN/NaS, it returns NOT_A_STRING_ID + //converts node to an existing string. If it doesn't exist or it's null, it returns NOT_A_STRING_ID static StringInternPool::StringID ToStringIDIfExists(EvaluableNode *e); //converts node to a string. Creates a reference to the string that must be destroyed, regardless of whether the string existed or not (if it did not exist, then it creates one) @@ -434,9 +418,10 @@ class EvaluableNode //transforms node to new_type, converting data if types are different // enm is used if it needs to allocate nodes when changing types + // if enm is nullptr, then it will not necessarily keep child nodes //if attempt_to_preserve_immediate_value is true, then it will try to preserve any relevant immediate value // attempt_to_preserve_immediate_value should be set to false if the value will be immediately overwritten - void SetType(EvaluableNodeType new_type, EvaluableNodeManager *enm, + void SetType(EvaluableNodeType new_type, EvaluableNodeManager *enm = nullptr, bool attempt_to_preserve_immediate_value = true); //fully clears node and sets it to new_type @@ -463,8 +448,15 @@ class EvaluableNode //sets the number value inline void SetNumberValue(double v) { - if(DoesEvaluableNodeTypeUseNumberData(GetType())) - GetNumberValueReference() = v; + if(FastIsNaN(v)) + { + SetType(ENT_NULL); + } + else + { + if(DoesEvaluableNodeTypeUseNumberData(GetType())) + GetNumberValueReference() = v; + } } //sets up the ability to contain a string @@ -834,7 +826,7 @@ class EvaluableNode { new (&mappedChildNodes) AssocType; } inline void DestructMappedChildNodes() - { + { string_intern_pool.DestroyStringReferences(mappedChildNodes, [](auto n) { return n.first; }); mappedChildNodes.~AssocType(); } @@ -850,11 +842,11 @@ class EvaluableNode { //string value StringInternPool::StringID stringID; - + //allow up to one label -- only used when not part of an extended value StringInternPool::StringID labelStringID; } stringValueContainer; - + //when type represents a number, holds the corresponding value struct EvaluableNodeValueNumber { @@ -963,7 +955,7 @@ enum EvaluableNodeImmediateValueType : uint8_t ENIVT_NUMBER_INDIRECTION_INDEX //not a real EvaluableNode type, but an index to some data structure that has a number }; -//structure that can hold the most immediate value type of an EvaluableNode +//structure that can hold the most immediate value type of an EvaluableNode // EvaluableNodeImmediateValueType can be used to communicate which type of data is being held union EvaluableNodeImmediateValue { @@ -1036,7 +1028,7 @@ union EvaluableNodeImmediateValue if(type_1 == ENIVT_NULL) return true; else if(type_1 == ENIVT_NUMBER) - return EqualIncludingNaN(value_1.number, value_2.number); + return (value_1.number == value_2.number); else if(type_1 == ENIVT_STRING_ID) return (value_1.stringID == value_2.stringID); else if(type_1 == ENIVT_NUMBER_INDIRECTION_INDEX) @@ -1046,11 +1038,9 @@ union EvaluableNodeImmediateValue } //returns true if it is a null or null equivalent - static constexpr bool IsNullEquivalent(EvaluableNodeImmediateValueType type, EvaluableNodeImmediateValue &value) + static constexpr bool IsNull(EvaluableNodeImmediateValueType type, EvaluableNodeImmediateValue &value) { - return (type == ENIVT_NULL - || (type == ENIVT_NUMBER && FastIsNaN(value.number)) - || (type == ENIVT_STRING_ID && value.stringID == string_intern_pool.NOT_A_STRING_ID)); + return type == ENIVT_NULL; } operator double() @@ -1091,13 +1081,27 @@ class EvaluableNodeImmediateValueWithType nodeValue.number = 0.0; } - constexpr EvaluableNodeImmediateValueWithType(double number) - : nodeType(ENIVT_NUMBER), nodeValue(number) - { } + __forceinline EvaluableNodeImmediateValueWithType(double number) + { + if(FastIsNaN(number)) + nodeType = ENIVT_NULL; + else + { + nodeType = ENIVT_NUMBER; + nodeValue = number; + } + } - constexpr EvaluableNodeImmediateValueWithType(StringInternPool::StringID string_id) - : nodeType(ENIVT_STRING_ID), nodeValue(string_id) - { } + __forceinline EvaluableNodeImmediateValueWithType(StringInternPool::StringID string_id) + { + if(string_id == StringInternPool::NOT_A_STRING_ID) + nodeType = ENIVT_NULL; + else + { + nodeType = ENIVT_STRING_ID; + nodeValue = string_id; + } + } constexpr EvaluableNodeImmediateValueWithType(EvaluableNode *code) : nodeType(ENIVT_CODE), nodeValue(code) @@ -1156,8 +1160,6 @@ class EvaluableNodeImmediateValueWithType { if(nodeValue.number == 0.0) return false; - if(FastIsNaN(nodeValue.number)) - return false; return true; } @@ -1202,12 +1204,7 @@ class EvaluableNodeImmediateValueWithType std::pair GetValueAsString() { if(nodeType == ENIVT_NUMBER) - { - if(FastIsNaN(nodeValue.number)) - return std::make_pair(false, ""); - return std::make_pair(true, EvaluableNode::NumberToString(nodeValue.number)); - } if(nodeType == ENIVT_STRING_ID) { @@ -1229,9 +1226,6 @@ class EvaluableNodeImmediateValueWithType { if(nodeType == ENIVT_NUMBER) { - if(FastIsNaN(nodeValue.number)) - return StringInternPool::NOT_A_STRING_ID; - const std::string str_value = EvaluableNode::NumberToString(nodeValue.number); //will return empty string if not found return string_intern_pool.GetIDFromString(str_value); @@ -1251,9 +1245,6 @@ class EvaluableNodeImmediateValueWithType { if(nodeType == ENIVT_NUMBER) { - if(FastIsNaN(nodeValue.number)) - return StringInternPool::NOT_A_STRING_ID; - const std::string str_value = EvaluableNode::NumberToString(nodeValue.number); //will return empty string if not found return string_intern_pool.CreateStringReference(str_value); @@ -1275,9 +1266,9 @@ class EvaluableNodeImmediateValueWithType } //returns true if it is a null or null equivalent - constexpr bool IsNullEquivalent() + constexpr bool IsNull() { - return EvaluableNodeImmediateValue::IsNullEquivalent(nodeType, nodeValue); + return EvaluableNodeImmediateValue::IsNull(nodeType, nodeValue); } EvaluableNodeImmediateValueType nodeType; diff --git a/src/Amalgam/evaluablenode/EvaluableNodeManagement.h b/src/Amalgam/evaluablenode/EvaluableNodeManagement.h index bb8f501b..08aeb0c9 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeManagement.h +++ b/src/Amalgam/evaluablenode/EvaluableNodeManagement.h @@ -33,8 +33,13 @@ class EvaluableNodeReference { } constexpr EvaluableNodeReference(double value) - : value(value), unique(true) - { } + : unique(true) + { + if(FastIsNaN(value)) + this->value = EvaluableNodeImmediateValueWithType(); + else + this->value = EvaluableNodeImmediateValueWithType(value); + } __forceinline EvaluableNodeReference(StringInternPool::StringID string_id) : value(string_intern_pool.CreateStringReference(string_id)), unique(true) @@ -80,7 +85,7 @@ class EvaluableNodeReference { if(value.nodeValue.code == nullptr) return false; - + return value.nodeValue.code->GetNeedCycleCheck(); } @@ -89,7 +94,7 @@ class EvaluableNodeReference { if(value.nodeValue.code == nullptr) return; - + value.nodeValue.code->SetNeedCycleCheck(need_cycle_check); } @@ -149,7 +154,7 @@ class EvaluableNodeReference protected: EvaluableNodeImmediateValueWithType value; - + public: //this is the only reference to the result @@ -195,7 +200,7 @@ class EvaluableNodeStackStateSaver std::vector *stack; size_t originalStackSize; }; - + class EvaluableNodeManager { public: @@ -291,7 +296,7 @@ class EvaluableNodeManager { return AllocNode(type, static_cast(sir)); } inline EvaluableNode *AllocNode(EvaluableNodeType type, StringInternWeakRef &siwr) { return AllocNode(type, static_cast(siwr)); } - + inline EvaluableNode *AllocNode(double float_value) { EvaluableNode *n = AllocUninitializedNode(); @@ -470,7 +475,7 @@ class EvaluableNodeManager //Copies the data structure and everything underneath it, modifying labels as specified // if cycle_free is true on input, then it can perform a faster copy inline EvaluableNodeReference DeepAllocCopy(EvaluableNode *tree, EvaluableNodeMetadataModifier metadata_modifier = ENMM_NO_CHANGE) - { + { if(tree == nullptr) return EvaluableNodeReference::Null(); @@ -585,7 +590,7 @@ class EvaluableNodeManager if(enr.unique && !enr.IsImmediateValue()) FreeNode(enr); } - + //frees all nodes void FreeAllNodes(); @@ -594,7 +599,7 @@ class EvaluableNodeManager { if(en == nullptr) return; - + if(IsEvaluableNodeTypeImmediate(en->GetType())) { en->Invalidate(); @@ -644,7 +649,7 @@ class EvaluableNodeManager if(e != nullptr) FreeNodeTreeRecurse(e); } - } + } ReclaimFreedNodesAtEnd(); } @@ -759,7 +764,7 @@ class EvaluableNodeManager //returns the number of nodes currently being used that have not been freed yet __forceinline size_t GetNumberOfUsedNodes() { return firstUnusedNodeIndex; } - + __forceinline size_t GetNumberOfUnusedNodes() { return nodes.size() - firstUnusedNodeIndex; } @@ -823,7 +828,7 @@ class EvaluableNodeManager //Returns all nodes still in use. For debugging purposes std::vector GetUsedNodes() { return std::vector(begin(nodes), begin(nodes) + firstUnusedNodeIndex); } - + //returns an estimate of the amount of memory allocated by the nodes managed // only an estimate because the platform's underlying memory management system may need to allocate additional // memory that cannot be easily accounted for diff --git a/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.cpp b/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.cpp index 017cdda0..f7156916 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.cpp +++ b/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.cpp @@ -176,7 +176,7 @@ EvaluableNode **GetRelativeEvaluableNodeFromTraversalPathList(EvaluableNode **so //fetch the new destination based on what is being fetched EvaluableNode *addr = index_path_nodes[i]; - bool addr_empty = EvaluableNode::IsEmptyNode(addr); + bool addr_empty = EvaluableNode::IsNull(addr); //if out of nodes but need to traverse further in the index, then will need to create new nodes if((*destination) == nullptr) @@ -310,7 +310,7 @@ EvaluableNodeReference AccumulateEvaluableNodeIntoEvaluableNode(EvaluableNodeRef //if the value is unique, then can just edit in place if(value_destination_node.unique) { - if(EvaluableNode::CanRepresentValueAsANumber(value_destination_node) && !EvaluableNode::IsNaN(value_destination_node)) + if(value_destination_node->GetType() == ENT_NUMBER) { double cur_value = EvaluableNode::ToNumber(value_destination_node); double inc_value = EvaluableNode::ToNumber(variable_value_node); @@ -346,7 +346,7 @@ EvaluableNodeReference AccumulateEvaluableNodeIntoEvaluableNode(EvaluableNodeRef value_destination_node->SetIsIdempotent(result_idempontent); value_destination_node.unique = result_unique; } - else if(value_destination_node->IsStringValue()) + else if(value_destination_node->GetType() == ENT_STRING) { auto [cur_value_valid, cur_value] = EvaluableNode::ToString(value_destination_node); auto [inc_value_valid, inc_value] = EvaluableNode::ToString(variable_value_node); @@ -395,7 +395,7 @@ EvaluableNodeReference AccumulateEvaluableNodeIntoEvaluableNode(EvaluableNodeRef } //not unique, so need to make a new list - if(EvaluableNode::CanRepresentValueAsANumber(value_destination_node) && !EvaluableNode::IsNaN(value_destination_node)) + if(value_destination_node->GetType() == ENT_NUMBER) { double cur_value = EvaluableNode::ToNumber(value_destination_node); double inc_value = EvaluableNode::ToNumber(variable_value_node); @@ -426,7 +426,7 @@ EvaluableNodeReference AccumulateEvaluableNodeIntoEvaluableNode(EvaluableNodeRef value_destination_node->SetIsIdempotent(result_idempontent); value_destination_node.SetReference(new_list, result_unique); } - else if(value_destination_node->IsStringValue()) + else if(value_destination_node->GetType() == ENT_STRING) { auto [cur_value_valid, cur_value] = EvaluableNode::ToString(value_destination_node); auto [inc_value_valid, inc_value] = EvaluableNode::ToString(variable_value_node); diff --git a/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.h b/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.h index 30df1b27..03c4eb8b 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.h +++ b/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.h @@ -146,7 +146,7 @@ TraverseToEntityReferenceAndContainerViaEvaluableNodeID(Entity *from_entity, EvaluableNode *id_node, StringInternRef *dest_sid_ref) { - if(EvaluableNode::IsEmptyNode(id_node)) + if(EvaluableNode::IsNull(id_node)) return std::make_pair(EntityReferenceType(from_entity), EntityReferenceType(nullptr)); //get the string id, get a reference if returning it @@ -245,7 +245,7 @@ TraverseToEntityReferenceAndContainerViaEvaluableNodeIDPath( if(from_entity == nullptr) return std::make_pair(EntityReferenceType(nullptr), EntityReferenceType(nullptr)); - if(EvaluableNode::IsEmptyNode(id_path) || id_path->GetType() != ENT_LIST) + if(EvaluableNode::IsNull(id_path) || id_path->GetType() != ENT_LIST) return TraverseToEntityReferenceAndContainerViaEvaluableNodeID(from_entity, id_path, dest_sid_ref); EvaluableNodeIDPathTraverser traverser(id_path, dest_sid_ref != nullptr); diff --git a/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.cpp b/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.cpp index 978986f0..73883a19 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.cpp +++ b/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.cpp @@ -39,26 +39,9 @@ EvaluableNodeTreeManipulation::NodesMixMethod::NodesMixMethod(RandomStream rando inline double MixNumberValues(double a, double b, double fraction_a, double fraction_b) { //quick exit for when they match - if(EqualIncludingNaN(a, b)) + if(a == b) return a; - //handle nans - if(FastIsNaN(a)) - { - if(fraction_a > 0) - return std::numeric_limits::quiet_NaN(); - else - return b; - } - - if(FastIsNaN(b)) - { - if(fraction_b > 0) - return std::numeric_limits::quiet_NaN(); - else - return a; - } - //normalize fractions fraction_a = fraction_a / (fraction_a + fraction_b); return a * fraction_a + b * (1 - fraction_a); @@ -110,7 +93,7 @@ EvaluableNode *EvaluableNodeTreeManipulation::NodesMixMethod::MergeValues(Evalua //if the original and merged, check to see if mergeable of same type, and if so, interpolate if(merged != nullptr && a != nullptr && b != nullptr) { - if(merged->IsNativelyNumeric() && a->IsNativelyNumeric() && b->IsNativelyNumeric()) + if(merged->IsNumericOrNull() && a->IsNumericOrNull() && b->IsNumericOrNull()) { double a_value = a->GetNumberValue(); double b_value = b->GetNumberValue(); @@ -1231,9 +1214,9 @@ std::pair EvaluableNodeTreeManipulation::CommonalityBet { double n1_value = n1->GetNumberValueReference(); double n2_value = n2->GetNumberValueReference(); - return std::make_pair(n1, EqualIncludingNaN(n1_value, n2_value) ? 1.0 : 0.0); + return std::make_pair(n1, n1_value == n2_value ? 1.0 : 0.0); } - if(n1->IsStringValue()) + if(n1_type == ENT_STRING) { auto n1_sid = n1->GetStringID(); auto n2_sid = n2->GetStringID(); @@ -1377,7 +1360,7 @@ std::pair EvaluableNodeTreeManipulation::CommonalityBet if(n2_type == ENT_NUMBER) { double n2_value = n2->GetNumberValueReference(); - if(EqualIncludingNaN(n1_value, n2_value)) + if(n1_value == n2_value) return std::make_pair(n1, 1.0); if(FastIsNaN(n1_value) || FastIsNaN(n2_value)) diff --git a/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.h b/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.h index bc35a9b0..7a4cbaed 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.h +++ b/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.h @@ -340,7 +340,7 @@ class EvaluableNodeTreeManipulation //returns a number between 0 and 1, where 1 is exactly the same and 0 is maximally different static inline double CommonalityBetweenNumbers(double n1, double n2) { - if(EqualIncludingNaN(n1, n2)) + if(n1 == n2) return 1.0; double commonality = NumberCommonality(std::fabs(n1 - n2), n1, n2); diff --git a/src/Amalgam/importexport/FileSupportCSV.cpp b/src/Amalgam/importexport/FileSupportCSV.cpp index 1114d3b9..6ef2b414 100644 --- a/src/Amalgam/importexport/FileSupportCSV.cpp +++ b/src/Amalgam/importexport/FileSupportCSV.cpp @@ -192,7 +192,7 @@ bool FileSupportCSV::Store(EvaluableNode *code, const std::string &resource_path is_first_column = false; //leave nulls blank - if(EvaluableNode::IsEmptyNode(column_node)) + if(EvaluableNode::IsNull(column_node)) continue; std::string original_string = EvaluableNode::ToStringPreservingOpcodeType(column_node); diff --git a/src/Amalgam/importexport/FileSupportYAML.cpp b/src/Amalgam/importexport/FileSupportYAML.cpp index 593f9a22..e3b0a9d1 100644 --- a/src/Amalgam/importexport/FileSupportYAML.cpp +++ b/src/Amalgam/importexport/FileSupportYAML.cpp @@ -61,6 +61,13 @@ EvaluableNode *YamlToEvaluableNodeRecurse(EvaluableNodeManager *enm, ryml::Const //returns true if it was able to create a yaml correctly, false if there was problematic data bool EvaluableNodeToYamlStringRecurse(EvaluableNode *en, ryml::NodeRef &built_element, bool sort_keys) { + //if null, don't set anything + if(en == nullptr) + { + built_element << nullptr; + return true; + } + if(en->IsAssociativeArray()) { built_element |= ryml::MAP; @@ -103,7 +110,7 @@ bool EvaluableNodeToYamlStringRecurse(EvaluableNode *en, ryml::NodeRef &built_el auto node_type = en->GetType(); if(node_type == ENT_NULL) { - //don't set anything + built_element << nullptr; return true; } else if(node_type == ENT_TRUE) diff --git a/src/Amalgam/interpreter/Interpreter.cpp b/src/Amalgam/interpreter/Interpreter.cpp index 011de77a..353d3914 100644 --- a/src/Amalgam/interpreter/Interpreter.cpp +++ b/src/Amalgam/interpreter/Interpreter.cpp @@ -548,7 +548,7 @@ EvaluableNode *Interpreter::GetCurrentCallStackContext() std::pair Interpreter::InterpretNodeIntoStringValue(EvaluableNode *n) { - if(EvaluableNode::IsEmptyNode(n)) + if(EvaluableNode::IsNull(n)) return std::make_pair(false, ""); //shortcut if the node has what is being asked @@ -604,7 +604,7 @@ StringInternPool::StringID Interpreter::InterpretNodeIntoStringIDValueWithRefere if(result.unique) { StringInternPool::StringID result_sid = string_intern_pool.NOT_A_STRING_ID; - if(result != nullptr && result->IsStringValue()) + if(result != nullptr && result->GetType() == ENT_STRING) result_sid = result->GetAndClearStringIDWithReference(); else result_sid = EvaluableNode::ToStringIDWithReference(result); @@ -718,7 +718,7 @@ EvaluableNode **Interpreter::TraverseToDestinationFromTraversalPathList(Evaluabl size_t address_list_length = 1; //if it's an actual address list, then use it - if(tpl != nullptr && DoesEvaluableNodeTypeUseOrderedData(tpl->GetType())) + if(!EvaluableNode::IsNull(tpl) && DoesEvaluableNodeTypeUseOrderedData(tpl->GetType())) { auto &ocn = tpl->GetOrderedChildNodes(); address_list = ocn.data(); diff --git a/src/Amalgam/interpreter/Interpreter.h b/src/Amalgam/interpreter/Interpreter.h index 19846b91..f93ed85e 100644 --- a/src/Amalgam/interpreter/Interpreter.h +++ b/src/Amalgam/interpreter/Interpreter.h @@ -427,7 +427,7 @@ class Interpreter if(curEntity == nullptr) return EntityReferenceType(nullptr); - if(EvaluableNode::IsEmptyNode(node_id_path_to_interpret)) + if(EvaluableNode::IsNull(node_id_path_to_interpret)) return EntityReferenceType(curEntity); //only need to interpret if not idempotent diff --git a/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp b/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp index 3f1cd7bb..bf620a2a 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesBase.cpp @@ -47,7 +47,7 @@ std::string GetEntityMemorySizeDiagnostics(Entity *e) { result += e->GetId() + " (used, free): " + EvaluableNode::NumberToString(cur_used - prev_used.first->second) + ", " + EvaluableNode::NumberToString(cur_unused - prev_unused.first->second) + "\n"; - + prev_used.first->second = cur_used; prev_unused.first->second = cur_unused; } @@ -108,7 +108,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_SYSTEM(EvaluableNode *en, std::string directory = InterpretNodeIntoStringValueEmptyNull(ocn[1]); std::filesystem::path path(directory); - + //try to set the directory std::error_code error; std::filesystem::current_path(directory, error); @@ -179,7 +179,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_SYSTEM(EvaluableNode *en, std::string rand_data(num_bytes, '\0'); Platform_GenerateSecureRandomData(&rand_data[0], num_bytes); - + return AllocReturn(rand_data, immediate_result); } else if(command == "sign_key_pair") @@ -497,7 +497,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL(EvaluableNode *en, bo new_context = InterpretNodeForImmediateUse(ocn[1]); PushNewCallStack(new_context); - + //call the code auto result = InterpretNode(function, immediate_result); @@ -694,7 +694,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LET(EvaluableNode *en, boo auto new_context = InterpretNodeForImmediateUse(ocn[0]); PushNewCallStack(new_context); - //run code + //run code EvaluableNodeReference result = EvaluableNodeReference::Null(); for(size_t i = 1; i < ocn_size; i++) { @@ -820,7 +820,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_DECLARE(EvaluableNode *en, SetTopCurrentIndexInConstructionStack(cn_id); EvaluableNodeReference value = InterpretNode(cn); - + #ifdef MULTITHREAD_SUPPORT //relock if needed before assigning the value if(need_write_lock) @@ -841,7 +841,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_DECLARE(EvaluableNode *en, //used to store the result or clear if possible EvaluableNodeReference result = EvaluableNodeReference::Null(); - //run code + //run code for(size_t i = 1; i < ocn_size; i++) { if(result.IsNonNullNodeReference()) @@ -970,13 +970,13 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_ASSIGN_and_ACCUM(Evaluable return EvaluableNodeReference::Null(); } - + //using a single variable StringInternRef variable_sid; variable_sid.SetIDWithReferenceHandoff(InterpretNodeIntoStringIDValueWithReference(ocn[0])); if(variable_sid == StringInternPool::NOT_A_STRING_ID) return EvaluableNodeReference::Null(); - + //if only 2 params and not accumulating, then just assign/accum the destination if(num_params == 2) { @@ -1032,7 +1032,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_ASSIGN_and_ACCUM(Evaluable //keeps track of whether each address is unique so they can be freed if relevant std::vector is_address_unique; - + //get each address/value pair to replace in result for(size_t ocn_index = 1; ocn_index + 1 < num_params; ocn_index += 2) { @@ -1136,7 +1136,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_RETRIEVE(EvaluableNode *en //need to return an assoc, so see if need to make copy evaluableNodeManager->EnsureNodeIsModifiable(to_lookup); - //overwrite values in the ordered + //overwrite values in the ordered for(auto &[cn_id, cn] : to_lookup->GetMappedChildNodesReference()) { //if there are values passed in, free them to be clobbered @@ -1152,7 +1152,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_RETRIEVE(EvaluableNode *en { evaluableNodeManager->EnsureNodeIsModifiable(to_lookup); - //overwrite values in the ordered + //overwrite values in the ordered for(auto &cn : to_lookup->GetOrderedChildNodes()) { StringInternPool::StringID symbol_name_sid = EvaluableNode::ToStringIDIfExists(cn); @@ -1203,7 +1203,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_GET(EvaluableNode *en, boo return EvaluableNodeReference(*target, source.unique); //only know about the target that it has similar properties to the source } - + //else, return a list for everything retrieved via get EvaluableNodeReference retrieved_list(evaluableNodeManager->AllocNode(ENT_LIST), source.unique); retrieved_list->ReserveOrderedChildNodes(ocn_size - 1); @@ -1361,7 +1361,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CURRENT_INDEX(EvaluableNod size_t offset = constructionStackIndicesAndUniqueness.size() - depth - 1; //build the index node to return - EvaluableNodeImmediateValueWithType enivwt = constructionStackIndicesAndUniqueness[offset].index; + EvaluableNodeImmediateValueWithType enivwt(constructionStackIndicesAndUniqueness[offset].index); if(enivwt.nodeType == ENIVT_NUMBER) return AllocReturn(enivwt.nodeValue.number, immediate_result); else if(enivwt.nodeType == ENIVT_STRING_ID) @@ -1578,10 +1578,10 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_RAND(EvaluableNode *en, bo //want to generate multiple values, so return a list EvaluableNodeReference retval(evaluableNodeManager->AllocNode(ENT_LIST), true); - + //just generate a list of values with replacement; either generate_unique_values was not set or the distribution "always" generates unique values retval->ReserveOrderedChildNodes(number_to_generate); - + //just get a bunch of random values with replacement bool can_free_param = true; for(size_t i = 0; i < number_to_generate; i++) diff --git a/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp b/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp index 243210ce..f27fa6c7 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp @@ -40,7 +40,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MUTATE(EvaluableNode *en, if(ocn.size() > 2) { auto opcode_weights_node = InterpretNodeForImmediateUse(ocn[2]); - if(!EvaluableNode::IsEmptyNode(opcode_weights_node)) + if(!EvaluableNode::IsNull(opcode_weights_node)) { ow_exists = true; for(auto &[node_id, node] : opcode_weights_node->GetMappedChildNodes()) @@ -55,7 +55,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MUTATE(EvaluableNode *en, if(ocn.size() > 3) { auto mutation_weights_node = InterpretNodeForImmediateUse(ocn[3]); - if(!EvaluableNode::IsEmptyNode(mutation_weights_node)) + if(!EvaluableNode::IsNull(mutation_weights_node)) { mtw_exists = true; for(auto &[node_id, node] : mutation_weights_node->GetMappedChildNodes()) @@ -349,7 +349,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MUTATE_ENTITY(EvaluableNod if(ocn.size() > 3) { auto opcode_weights_node = InterpretNodeForImmediateUse(ocn[3]); - if(!EvaluableNode::IsEmptyNode(opcode_weights_node)) + if(!EvaluableNode::IsNull(opcode_weights_node)) { ow_exists = true; for(auto &[node_id, node] : opcode_weights_node->GetMappedChildNodes()) @@ -364,7 +364,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MUTATE_ENTITY(EvaluableNod if(ocn.size() > 4) { auto mutation_weights_node = InterpretNodeForImmediateUse(ocn[4]); - if(!EvaluableNode::IsEmptyNode(mutation_weights_node)) + if(!EvaluableNode::IsNull(mutation_weights_node)) { mtw_exists = true; for(auto &[node_id, node] : mutation_weights_node->GetMappedChildNodes()) diff --git a/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp b/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp index 410b5ac2..14cc25c5 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp @@ -109,10 +109,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_ASSOC(EvaluableNode *en, b { //if idempotent, can just return a copy without any metadata if(en->GetIsIdempotent()) - { - EvaluableNodeReference retval = evaluableNodeManager->DeepAllocCopy(en, EvaluableNodeManager::ENMM_REMOVE_ALL); - return retval; - } + return evaluableNodeManager->DeepAllocCopy(en, EvaluableNodeManager::ENMM_REMOVE_ALL); //create a new assoc from the previous EvaluableNodeReference new_assoc(evaluableNodeManager->AllocNode(en, EvaluableNodeManager::ENMM_REMOVE_ALL), true); @@ -552,11 +549,11 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FORMAT(EvaluableNode *en, auto &mcn = from_params->GetMappedChildNodesReference(); auto found_locale = mcn.find(ENBISI_locale); - if(found_locale != end(mcn) && !EvaluableNode::IsEmptyNode(found_locale->second)) + if(found_locale != end(mcn) && !EvaluableNode::IsNull(found_locale->second)) locale = EvaluableNode::ToStringPreservingOpcodeType(found_locale->second); auto found_timezone = mcn.find(ENBISI_timezone); - if(found_timezone != end(mcn) && !EvaluableNode::IsEmptyNode(found_timezone->second)) + if(found_timezone != end(mcn) && !EvaluableNode::IsNull(found_timezone->second)) timezone = EvaluableNode::ToStringPreservingOpcodeType(found_timezone->second); } @@ -871,11 +868,11 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FORMAT(EvaluableNode *en, auto &mcn = to_params->GetMappedChildNodesReference(); auto found_locale = mcn.find(ENBISI_locale); - if(found_locale != end(mcn) && !EvaluableNode::IsEmptyNode(found_locale->second)) + if(found_locale != end(mcn) && !EvaluableNode::IsNull(found_locale->second)) locale = EvaluableNode::ToStringPreservingOpcodeType(found_locale->second); auto found_timezone = mcn.find(ENBISI_timezone); - if(found_timezone != end(mcn) && !EvaluableNode::IsEmptyNode(found_timezone->second)) + if(found_timezone != end(mcn) && !EvaluableNode::IsNull(found_timezone->second)) timezone = EvaluableNode::ToStringPreservingOpcodeType(found_timezone->second); } @@ -1392,7 +1389,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_SUBSTR(EvaluableNode *en, } //if a number, then go by offset - if(substr_node->IsNativelyNumeric()) + if(substr_node->IsNumericOrNull()) { double start_offset_raw = EvaluableNode::ToNumber(substr_node); evaluableNodeManager->FreeNodeTreeIfPossible(substr_node); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp b/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp index 91db00ef..0831139d 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesListManipulation.cpp @@ -88,9 +88,6 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FIRST(EvaluableNode *en, b { //return 0 if zero double value = list->GetNumberValueReference(); - if(FastIsNaN(value)) - return AllocReturn(std::numeric_limits::quiet_NaN(), immediate_result); - if(value == 0.0) return list; diff --git a/src/Amalgam/interpreter/InterpreterOpcodesLogic.cpp b/src/Amalgam/interpreter/InterpreterOpcodesLogic.cpp index 8a06305e..64c7efda 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesLogic.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesLogic.cpp @@ -335,7 +335,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LESS_and_LEQUAL(EvaluableN if(InterpretEvaluableNodesConcurrently(en, ocn, interpreted_nodes)) { EvaluableNodeReference prev = interpreted_nodes[0]; - if(EvaluableNode::IsNaN(prev)) + if(EvaluableNode::IsNull(prev)) { for(auto &n : interpreted_nodes) evaluableNodeManager->FreeNodeTreeIfPossible(n); @@ -349,7 +349,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LESS_and_LEQUAL(EvaluableN //if not in strict increasing order, return false auto &cur = interpreted_nodes[i]; - if(EvaluableNode::IsNaN(cur)) + if(EvaluableNode::IsNull(cur)) { result = false; break; @@ -370,7 +370,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LESS_and_LEQUAL(EvaluableN #endif auto prev = InterpretNodeForImmediateUse(ocn[0]); - if(EvaluableNode::IsEmptyNode(prev)) + if(EvaluableNode::IsNull(prev)) return evaluableNodeManager->ReuseOrAllocNode(prev, ENT_FALSE); auto node_stack = CreateInterpreterNodeStackStateSaver(prev); @@ -380,7 +380,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LESS_and_LEQUAL(EvaluableN //if not in strict increasing order, return false auto cur = InterpretNodeForImmediateUse(ocn[i]); - if(EvaluableNode::IsEmptyNode(cur) + if(EvaluableNode::IsNull(cur) || !EvaluableNode::IsLessThan(prev, cur, en->GetType() == ENT_LEQUAL)) return ReuseOrAllocOneOfReturn(prev, cur, false, immediate_result); @@ -411,7 +411,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_GREATER_and_GEQUAL(Evaluab if(InterpretEvaluableNodesConcurrently(en, ocn, interpreted_nodes)) { EvaluableNodeReference prev = interpreted_nodes[0]; - if(EvaluableNode::IsNaN(prev)) + if(EvaluableNode::IsNull(prev)) { for(auto &n : interpreted_nodes) evaluableNodeManager->FreeNodeTreeIfPossible(n); @@ -425,7 +425,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_GREATER_and_GEQUAL(Evaluab //if not in strict increasing order, return false auto &cur = interpreted_nodes[i]; - if(EvaluableNode::IsNaN(cur)) + if(EvaluableNode::IsNull(cur)) { result = false; break; @@ -446,7 +446,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_GREATER_and_GEQUAL(Evaluab #endif auto prev = InterpretNodeForImmediateUse(ocn[0]); - if(EvaluableNode::IsEmptyNode(prev)) + if(EvaluableNode::IsNull(prev)) return evaluableNodeManager->ReuseOrAllocNode(prev, ENT_FALSE); auto node_stack = CreateInterpreterNodeStackStateSaver(prev); @@ -456,7 +456,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_GREATER_and_GEQUAL(Evaluab //if not in strict increasing order, return false auto cur = InterpretNodeForImmediateUse(ocn[i]); - if(EvaluableNode::IsEmptyNode(cur) + if(EvaluableNode::IsNull(cur) || !EvaluableNode::IsLessThan(cur, prev, en->GetType() == ENT_GEQUAL)) return ReuseOrAllocOneOfReturn(prev, cur, false, immediate_result); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp b/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp index 6d25e785..6df69ff4 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesTransformations.cpp @@ -376,7 +376,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FILTER(EvaluableNode *en, std::vector ids_to_remove; for(auto &[cn_id, cn] : result_list_mcn) { - if(EvaluableNode::IsEmptyNode(cn)) + if(EvaluableNode::IsNull(cn)) ids_to_remove.push_back(cn_id); } @@ -407,7 +407,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FILTER(EvaluableNode *en, for(size_t i = result_list_ocn.size(); i > 0; i--) { size_t index = i - 1; - if(!EvaluableNode::IsEmptyNode(result_list_ocn[index])) + if(!EvaluableNode::IsNull(result_list_ocn[index])) continue; evaluableNodeManager->FreeNodeTree(result_list_ocn[index]); @@ -417,7 +417,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FILTER(EvaluableNode *en, else //can't safely delete any nodes { auto new_end = std::remove_if(begin(result_list_ocn), end(result_list_ocn), - [](EvaluableNode *en) { return EvaluableNode::IsEmptyNode(en); }); + [](EvaluableNode *en) { return EvaluableNode::IsNull(en); }); result_list_ocn.erase(new_end, end(result_list_ocn)); } } @@ -717,7 +717,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_REDUCE(EvaluableNode *en, return EvaluableNodeReference::Null(); auto function = InterpretNodeForImmediateUse(ocn[0]); - if(EvaluableNode::IsEmptyNode(function)) + if(EvaluableNode::IsNull(function)) return EvaluableNodeReference::Null(); auto node_stack = CreateInterpreterNodeStackStateSaver(function); @@ -875,7 +875,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_SORT(EvaluableNode *en, bo { //get list auto list = InterpretNode(ocn[list_index]); - if(EvaluableNode::IsEmptyNode(list)) + if(EvaluableNode::IsNull(list)) return EvaluableNodeReference::Null(); //make sure it is an editable copy @@ -923,7 +923,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_SORT(EvaluableNode *en, bo //get list auto list = InterpretNode(ocn[list_index]); - if(EvaluableNode::IsEmptyNode(list)) + if(EvaluableNode::IsNull(list)) return EvaluableNodeReference::Null(); //make sure it is an editable copy @@ -1154,7 +1154,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CONTAINS_VALUE(EvaluableNo } } } - else if(container->GetType() == ENT_STRING && !EvaluableNode::IsEmptyNode(value)) + else if(container->GetType() == ENT_STRING && !EvaluableNode::IsNull(value)) { //compute regular expression std::string s = container->GetStringValue(); @@ -1581,7 +1581,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_ZIP(EvaluableNode *en, boo //attempt to get indices, the keys of the assoc auto index_list = InterpretNodeForImmediateUse(ocn[index_list_index]); - if(EvaluableNode::IsEmptyNode(index_list)) + if(EvaluableNode::IsNull(index_list)) { EvaluableNodeReference result(evaluableNodeManager->AllocNode(ENT_ASSOC), true); return result; @@ -1686,7 +1686,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_UNZIP(EvaluableNode *en, b return EvaluableNodeReference::Null(); auto zipped = InterpretNode(ocn[0]); - if(EvaluableNode::IsEmptyNode(zipped)) + if(EvaluableNode::IsNull(zipped)) return EvaluableNodeReference(evaluableNodeManager->AllocNode(ENT_LIST), true); auto node_stack = CreateInterpreterNodeStackStateSaver(zipped); @@ -1695,7 +1695,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_UNZIP(EvaluableNode *en, b EvaluableNodeReference result(evaluableNodeManager->AllocNode(ENT_LIST), true); - if(EvaluableNode::IsEmptyNode(index_list)) + if(EvaluableNode::IsNull(index_list)) return result; auto &index_list_ocn = index_list->GetOrderedChildNodes(); @@ -1730,10 +1730,10 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_UNZIP(EvaluableNode *en, b index_value = 0; } - if(FastIsNaN(index_value) || index_value >= zipped_ocn.size()) - result_ocn.push_back(nullptr); - else + if(index_value < zipped_ocn.size()) result_ocn.push_back(zipped_ocn[static_cast(index_value)]); + else + result_ocn.push_back(nullptr); } } diff --git a/src/Amalgam/out.txt b/src/Amalgam/out.txt index f741044c..2472791f 100644 --- a/src/Amalgam/out.txt +++ b/src/Amalgam/out.txt @@ -233,13 +233,13 @@ notakeyword } --parse and unparse-- (print "hello") -[.nan .nan .infinity -.infinity] +[(null) (null) .infinity -.infinity] -{a 1 c ["alpha" "beta" "gamma"] b 2} +{c ["alpha" "beta" "gamma"] b 2 a 1} { - a 1 c ["alpha" "beta" "gamma"] b 2 + a 1 } --if-- @@ -261,7 +261,8 @@ if 2 ) --call_sandboxed-- 7 -.nan +(null) + --while-- 1 2 @@ -517,7 +518,8 @@ abcdef --cosh-- 1.1276259652063807 --acosh-- -.nan +(null) + --tanh-- 0.46211715726000974 --atanh-- @@ -575,7 +577,8 @@ abcdef 26 2.23606797749979 27 3.0000000031604355 28 3 -29 .nan +29 (null) + 30 6 31 4 32 4 @@ -601,11 +604,12 @@ abcdef 0.14384103622589045 --first-- 4 -1 +2 1 0 a -.nas +(null) + --tail-- [9.2 "this"] [2 3 4 5 6] @@ -617,17 +621,17 @@ a [] { a 1 - c 3 + b 2 d 4 e 5 f 6 } -{d 4 f 6} +{d 4 e 5} { a 1 + b 2 d 4 e 5 - f 6 } { a 1 @@ -647,14 +651,16 @@ abcdef abcdef -.nas +(null) + --last-- this -1 +2 1 0 c -.nas +(null) + --trunc-- [4 9.2] [1 2 3 4 5] @@ -666,17 +672,17 @@ c [] { a 1 - c 3 + b 2 d 4 e 5 f 6 } -{d 4 f 6} +{d 4 e 5} { a 1 + b 2 d 4 e 5 - f 6 } { a 1 @@ -696,7 +702,8 @@ abcdef abcdef -.nas +(null) + --append-- [ 1 @@ -820,13 +827,13 @@ abcdef 40 44 } [3 4 5 6 7 8] -[3 4 5 6 7 .nan] +[3 4 5 6 7 (null)] { 0 3 - 1 .nan - 2 .nan - 3 .nan - a .nan + 1 (null) + 2 (null) + 3 (null) + a (null) } --filter-- [3 4] @@ -1026,7 +1033,7 @@ abcdef [1 3] [9 5] --indices-- -["a" "4" "c" "b"] +["c" "4" "b" "a"] [ 0 1 @@ -1038,7 +1045,7 @@ abcdef 7 ] --values-- -[1 "d" 3 2] +[3 "d" 2 1] [ "a" 1 @@ -1059,7 +1066,7 @@ abcdef 4 "d" ] -[2 1 "d" 0 3] +[3 "d" 2 1 0] [ 1 2 @@ -1256,7 +1263,7 @@ current_index: 2 8 ] accum_string "abcdef" - argv ["C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] + argv ["C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] bar (declare {x 6} (+ x 2) @@ -1269,10 +1276,10 @@ current_index: 2 A {B 2} B 2 } - interpreter "C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" + interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1718313300.251917 + start_time 1719346356.42301 www 1 x 12 zz 10 @@ -1299,7 +1306,7 @@ current_index: 2 8 ] accum_string "abcdef" - argv ["C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] + argv ["C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] bar (declare {x 6} (+ x 2) @@ -1312,10 +1319,10 @@ current_index: 2 A {B 2} B 2 } - interpreter "C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" + interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1718313300.251917 + start_time 1719346356.42301 www 1 x 12 zz 10 @@ -1341,7 +1348,7 @@ current_index: 2 8 ] accum_string "abcdef" - argv ["C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] + argv ["C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\src\\Amalgam\\./amlg_code/full_test.amlg"] bar (declare {x 6} (+ x 2) @@ -1354,10 +1361,10 @@ current_index: 2 A {B 2} B 2 } - interpreter "C:\\Users\\ChristopherHazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" + interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1718313300.251917 + start_time 1719346356.42301 www 1 x 12 zz 10 @@ -1490,22 +1497,22 @@ true --weighted_rand-- b -["b" "b" "a" "b"] +["b" "b" "b" "b"] b ["a" "b" @(get (target 0) 0) @(get (target 0) 1)] ["a" @(get (target 0) 0) "b" @(get (target 0) 2)] -infinity test c or d: ["c" "c" "c" "d"] +infinity test c or d: ["d" "d" "d" "c"] infinity test c or d: ["c" @(get (target 0) 0) "d" @(get (target 0) 0)] -{a 25 b 45 c 30} +{a 24 b 51 c 25} {a 29 b 44 c 27} -["1" "2" "3"] +["3" "2" "1"] --get_rand_seed-- ȼ\KOaVT z @@ -1532,8 +1539,6 @@ infinity test c or d: ["c" @(get (target 0) 0) "d" @(get (target 0) 0)] --infinity-- .infinity -.infinity ---nan-- -.nan --list-- ["a" 1 "b"] --associate-- @@ -1552,8 +1557,8 @@ infinity test c or d: ["c" @(get (target 0) 0) "d" @(get (target 0) 0)] string --set_type-- (- 3 4) -["a" 4 "b" 3] -["a" 4 "b" 3] +["b" 3 "a" 4] +["b" 3 "a" 4] {a 4 b 3} 8.7 (parallel @@ -1604,15 +1609,15 @@ string ] 21: [{"b":4,"a":3},{"c":"c","d":null}] 22: [{"a":3,"b":4},{"c":"c","d":null}] -23: b: 2 +23: c: 3 +b: 2 a: 1 e: - a - b - - .nan + - - .inf d: 4 -c: 3 24: a: 1 b: 2 @@ -1621,11 +1626,11 @@ d: 4 e: - a - b - - .nan + - - .inf 25: {a 1} -current date-time in epoch: 2024-06-13-17.15.00.3051410 +current date-time in epoch: 2024-06-25-16.12.36.7313240 2020-06-07 00:22:59 1391230800 1391230800 @@ -1675,7 +1680,7 @@ domingo, jun. 07, 2020 ) } { - labelA #labelQ #labelA + labelA #labelA #labelQ (lambda #labelB (true) ) @@ -1692,7 +1697,7 @@ domingo, jun. 07, 2020 ) } { - labelA #labelQ #labelA + labelA #labelA #labelQ (lambda #labelB (true) ) @@ -1890,7 +1895,6 @@ decrypted: hello -1.23456789e-05 -1.2345e-149 ] -.nan .infinity (true) @@ -1914,26 +1918,29 @@ decrypted: hello b a (declare) - (associate "a" 1) + (get "a") ] [ + (+) (*) - 2 3 - (*) - (associate "alpha" 5 "beta" 6) + 4 + (associate) (associate "nest" - (associate "count" []) + (associate + "count" + [7 8 9] + ) "end" - [10 11 12] + [(-) 11 12] ) ] --commonality-- 3 15 0.25 -0.25 +0.375 1 0.25 1 @@ -2167,16 +2174,6 @@ decrypted: hello {_ (null)} (replace _ - ["g"] - (lambda - [ - (get - (current_value 1) - 0 - ) - 4 - ] - ) [] (lambda { @@ -2187,6 +2184,16 @@ decrypted: hello ) } ) + ["g"] + (lambda + [ + (get + (current_value 1) + 0 + ) + 4 + ] + ) ) ) (declare @@ -2361,7 +2368,15 @@ decrypted: hello ] ] --mix-- -[3.5 5.5 7.5 9.5 11.5 13.5] +[ + 3.5 + 6 + 5 + 7.5 + 9.5 + 11.5 + 13.5 +] [ ;comment 1 @@ -2370,11 +2385,11 @@ decrypted: hello ;comment 4 1 3.5 - 5 + 5.5 8 9.5 11.5 - 13.5 + 13 ] [ 1 @@ -2383,19 +2398,11 @@ decrypted: hello (associate "a" 3 "b" 4) (lambda (if - true + false 1 - (parallel - (get_entity_comments) - (lambda - (print - [2] - ) - ) - ) + (parallel (get_entity_comments) 1) ) ) - [5 6] ] [ 1 @@ -2406,12 +2413,20 @@ decrypted: hello (if true 1 - (seq (get_entity_comments) 1) + (seq + (get_entity_comments) + (lambda + (print + [2 9] + ) + ) + ) ) ) - [5 6] + [6] ] [ + (true) 2 3.5 5.5 @@ -2421,23 +2436,22 @@ decrypted: hello 13.5 ] [ - (true) - 4 + 2 + 3 + 6 5 - 8 - 7 10 - 11 + 12 14 13 ] -4 1 2.5 2.5 -abcdmxyz -abcemxyz -abcomxyz +2.5 +abcdexyz +abcdeomxyz +abcdexyz --mix_labels-- [ 1 @@ -2598,7 +2612,7 @@ flatten restore without seeds test (assign_entity_roots new_entity _) ) ) - (set_entity_rand_seed new_entity "R} ]ْͫ") + (set_entity_rand_seed new_entity "1]*|#") (set_entity_rand_seed (first (create_entities @@ -2610,7 +2624,7 @@ flatten restore without seeds test ) ) ) - "Qv&rS ˙" + "K2aY" ) new_entity ) @@ -2702,56 +2716,56 @@ flatten restore with parallel --mutate_entity-- [ 1 + 2 (mix_labels) - 3 + 4 (acos) a (contains_label) - 4 5 - 9 + 5 + 10 (<) (abs) - 12 13 - 0.06646554813776002 - (associate "a" (get) "b" 2) + 14 + (associate) ] [ + 1 + (get) + 3 + 4 (or) - 14 + 6 b (and) - 5 + 9 (query_sample) - 7 - 8 - (sin) - 10 11 12 - 13 - (zip) - (associate "a" (map) "b" (assign)) + (sin) + 14 + (associate "a" 1 (get) 2) ] [ 1 - (-) + 2 3 - 4 + (-) 5 (+) 7 8 - (+) - (+) - 11 (-) - 13 + 10 + 11 + 12 + (+) 14 - (associate "a" (*) "b" 2) + (associate (+) (+) "b" (-)) ] --commonality_entities-- @@ -2811,26 +2825,26 @@ _2280722175 ##p ["_3990396532" "_3990396532" "_3330773578" "_3330773578"] ) -_3330773578 +_3990396532 (associate - "e" + "E" 3 - "f" + "F" 4 - "g" + "G" 5 - "h" + "H" 6 ) -_3990396532 +_3330773578 (associate - "E" + "e" 3 - "F" + "f" 4 - "G" + "g" 5 - "H" + "h" 6 ) --difference_entities-- @@ -3344,31 +3358,22 @@ difference between DiffContainer and DiffEntity2: --mix_entities-- (associate "b" 4 "a" 3) MergeEntityChild1 -(associate "x" 3 "y" 4 "z" 5) +(associate "x" 3 "y" 4) MergeEntityChild2 -(associate - "p" - 3 - "q" - 4 - "u" - 5 - "w" - 7 -) +(associate "p" 3 "q" 4 "u" 5) _2169689611 +(associate "e" 3 "f" 4) +_2280722175 (associate - "e" + "E" 3 - "f" + "F" 4 - "g" + "G" 5 - "h" + "H" 6 ) -_2280722175 -(associate "E" 3 "F" 4) --get_entity_comments-- Full test This is a suite of unit tests. @@ -3446,7 +3451,7 @@ deep sets --set_entity_root_permission-- RootTest -1718313300.404958 +1719346356.940955 (true) RootTest @@ -3849,74 +3854,74 @@ store to .json normally ["Child2" "Child7"] ["Child1" "Child5"] ["Child3" "Child4"] -["Child1" "Child3" "Child4" "Child6"] -["Child2" "Child3" "Child5" "Child7"] +["Child4" "Child5" "Child6" "Child7"] +["Child2" "Child4" "Child5" "Child6"] ["Child4" "Child6"] --query_sample-- +["Child6"] +["Child5" "Child3"] +["Child3"] +["Child6"] +--query_weighted_sample-- ["Child2"] -["Child1" "Child6"] -["Child7"] ["Child4"] ---query_weighted_sample-- -["Child1"] -["Child1"] [ - "Child2" - "Child2" - "Child2" - "Child2" - "Child2" - "Child2" - "Child1" - "Child5" "Child1" "Child2" "Child2" "Child1" - "Child2" "Child1" "Child2" "Child1" "Child2" "Child1" + "Child2" "Child1" "Child1" -] -[ - "Child1" "Child1" "Child2" "Child2" "Child2" + "Child4" + "Child1" + "Child2" "Child1" +] +[ "Child1" "Child1" "Child1" - "Child6" "Child2" "Child2" "Child1" "Child2" + "Child2" + "Child1" "Child1" "Child2" "Child1" "Child1" + "Child1" + "Child1" + "Child2" + "Child2" + "Child2" + "Child5" "Child2" - "Child3" ] [ "Child2" "Child2" "Child2" - "Child3" - "Child6" + "Child2" + "Child2" "Child2" "Child2" "Child2" "Child2" "Child2" ] -["Child2" "Child3" "Child5"] +["Child2"] --query_in_entity_list-- ["Child6" "Child7"] --query_not_in_entity_list-- @@ -3976,7 +3981,7 @@ weighted query: { Child7 0.2 } weighted query list of lists: [ - ["Child6" "Child7" "Child2" "Child1" "Child3"] + ["Child6" "Child7" "Child2" "Child1" "Child4"] [0.04 0.2 0.45 1.8 2] ] weighted query list of lists: [ @@ -3985,7 +3990,7 @@ weighted query list of lists: [ [-1 2 4 10 100] ] weighted query list of lists with multiple values: [ - ["Child2" "Child6" "Child1" "Child7" "Child3"] + ["Child2" "Child6" "Child1" "Child7" "Child4"] [1 2 4 10 100] [-1 2 4 10 100] [-1 1 3 0 100] @@ -4430,14 +4435,14 @@ a ) 1 ] ---nan equality tests-- -(= (null) .nan): (false) +--null equality tests-- +(= (null) (null)): (true) -(= (+ (null)) .nan): (true) +(= (+ (null)) (null)): (true) ["Entity3"] -["EntityNaN"] -["EntityNull"] +["EntityNull" "EntityNaN"] +["EntityNull" "EntityNaN"] ["Entity3" "EntityNull" "EntityNaN"] --combo tests-- 4.5 @@ -4455,7 +4460,7 @@ a ["hello" "!"] {a1 1.4142135623730951 a2 2 a3 1.4142135623730951} {a1 1.4142135623730951 a3 1.4142135623730951} -{a3 1.4142135623730951} +{a1 1.4142135623730951} {a1 5.0990195135927845 a2 2 a3 5.0990195135927845} {a1 1 a3 1 a4 0} --accuracy tests-- @@ -4467,11 +4472,11 @@ distance symmetry tests [ [ "B" + "C" + "F" "I" "D" "A" - "C" - "F" "E" "H" ] @@ -4489,12 +4494,12 @@ distance symmetry tests [ [ "B" - "D" - "A" + "C" "F" + "A" "I" - "C" - "J" + "D" + "G" "E" ] [ @@ -4689,4 +4694,4 @@ concurrent entity writes successful: (true) --clean-up test files-- --total execution time-- -1.3872911930084229 +1.2933847904205322 diff --git a/src/Amalgam/string/StringManipulation.cpp b/src/Amalgam/string/StringManipulation.cpp index e74be169..3c0c365b 100644 --- a/src/Amalgam/string/StringManipulation.cpp +++ b/src/Amalgam/string/StringManipulation.cpp @@ -15,7 +15,7 @@ std::string StringManipulation::NumberToString(double value) { //first check for unusual values if(FastIsNaN(value)) - return ".nan"; + return "(null)"; if(value == std::numeric_limits::infinity()) return ".infinity"; if(value == -std::numeric_limits::infinity())