excel_to_code 0.3.18.beta.1 → 0.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/src/commands/excel_to_x.rb +13 -4
- data/src/compile/c/excel_to_c_runtime.c +255 -3
- data/src/compile/c/excel_to_c_runtime_test.c +38 -0
- data/src/compile/c/map_formulae_to_c.rb +3 -0
- data/src/compile/ruby/map_formulae_to_ruby.rb +2 -1
- data/src/excel/excel_functions/mround.rb +19 -0
- data/src/excel/excel_functions/sumifs.rb +1 -1
- data/src/excel/excel_functions/text.rb +4 -1
- data/src/excel/excel_functions.rb +2 -0
- data/src/excel/reference.rb +1 -1
- data/src/rewrite/ast_expand_array_formulae.rb +4 -0
- data/src/rewrite/rewrite_array_formulae.rb +2 -2
- data/src/simplify/replace_arrays_with_single_cells.rb +30 -0
- data/src/simplify/replace_common_elements_in_formulae.rb +1 -1
- data/src/simplify/replace_named_references.rb +16 -1
- data/src/simplify/replace_offsets_with_references.rb +39 -7
- data/src/util.rb +1 -2
- data/src/version.rb +1 -1
- metadata +9 -13
- data/src/compile/c/a.out +0 -0
- data/src/compile/c/a.out.dSYM/Contents/Info.plist +0 -20
- data/src/compile/c/a.out.dSYM/Contents/Resources/DWARF/a.out +0 -0
- data/src/util/try.rb +0 -9
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 6fe18bdd631dff8de227380800892b66163f2fe7f130891440c7f2e12818a28f
         | 
| 4 | 
            +
              data.tar.gz: f0087a3035784f3fedee75f30064beb7f359e7e0f6d79ff0789f6a23aa5ad908
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 95f10de60d4ab71095c011ef1857d3467d2d1c88993e75aa560dd16293ee7448c98677715e9fca7fdb937c2793f64a74d20f746cfcfa30d0a636cc9c2eb31587
         | 
| 7 | 
            +
              data.tar.gz: 945373d97eb9523d8e6c3151249822af8bc55f3453ded5182d6979dbd5392b35ad1a4919fff367211330304e02164f59b640c6758e3978926b346ef9c7a6df0a
         | 
    
        data/src/commands/excel_to_x.rb
    CHANGED
    
    | @@ -342,7 +342,7 @@ class ExcelToX | |
| 342 342 | 
             
                  begin
         | 
| 343 343 | 
             
                    parsed = CachingFormulaParser.parse(reference, treat_external_references_as_local)
         | 
| 344 344 | 
             
                    if parsed
         | 
| 345 | 
            -
                      @named_references[name] = parsed
         | 
| 345 | 
            +
                      @named_references[name] = deep_copy(parsed)
         | 
| 346 346 | 
             
                    else
         | 
| 347 347 | 
             
                      $stderr.puts "Named reference #{name} #{reference} not parsed"
         | 
| 348 348 | 
             
                      exit
         | 
| @@ -363,6 +363,15 @@ class ExcelToX | |
| 363 363 |  | 
| 364 364 | 
             
              end
         | 
| 365 365 |  | 
| 366 | 
            +
            	def deep_copy(ast)
         | 
| 367 | 
            +
            		return ast if ast.is_a?(Symbol)
         | 
| 368 | 
            +
            		return ast if ast.is_a?(Numeric)
         | 
| 369 | 
            +
            		return ast.dup unless ast.is_a?(Array)
         | 
| 370 | 
            +
            		ast.map do |a|
         | 
| 371 | 
            +
            			deep_copy(a)
         | 
| 372 | 
            +
            		end
         | 
| 373 | 
            +
            	end
         | 
| 374 | 
            +
             | 
| 366 375 | 
             
              # Named references can be simple cell references, 
         | 
| 367 376 | 
             
              # or they can be ranges, or errors, or table references
         | 
| 368 377 | 
             
              # this function converts all the different types into
         | 
| @@ -375,7 +384,7 @@ class ExcelToX | |
| 375 384 | 
             
                @named_references.each do |name, reference|
         | 
| 376 385 | 
             
                  reference = table_reference_replacer.map(reference)
         | 
| 377 386 | 
             
                  reference = @replace_ranges_with_array_literals_replacer.map(reference)
         | 
| 378 | 
            -
                  @named_references[name] = reference
         | 
| 387 | 
            +
                  @named_references[name] = deep_copy(reference)
         | 
| 379 388 | 
             
                end
         | 
| 380 389 |  | 
| 381 390 | 
             
              end
         | 
| @@ -613,7 +622,7 @@ class ExcelToX | |
| 613 622 | 
             
              end
         | 
| 614 623 |  | 
| 615 624 | 
             
              def check_all_functions_implemented
         | 
| 616 | 
            -
                functions_that_are_removed_during_compilation = [:INDIRECT, :OFFSET, :ROW, :COLUMN, :TRANSPOSE]
         | 
| 625 | 
            +
                functions_that_are_removed_during_compilation = [:INDIRECT, :OFFSET, :ROW, :COLUMN, :TRANSPOSE, :'_xlfn.SINGLE']
         | 
| 617 626 | 
             
                functions_used = CachingFormulaParser.instance.functions_used.keys
         | 
| 618 627 | 
             
                functions_used.delete_if do |f|
         | 
| 619 628 | 
             
                  MapFormulaeToRuby::FUNCTIONS[f]
         | 
| @@ -1133,7 +1142,7 @@ class ExcelToX | |
| 1133 1142 | 
             
                  replacements_made_in_the_last_pass += indirect_replacement.count_replaced
         | 
| 1134 1143 |  | 
| 1135 1144 | 
             
                  log.info "Pass #{number_of_passes}: Made #{replacements_made_in_the_last_pass} replacements"
         | 
| 1136 | 
            -
                end while replacements_made_in_the_last_pass > 0 && number_of_passes <  | 
| 1145 | 
            +
                end while replacements_made_in_the_last_pass > 0 && number_of_passes < 50
         | 
| 1137 1146 | 
             
              end
         | 
| 1138 1147 |  | 
| 1139 1148 |  | 
| @@ -86,6 +86,7 @@ static ExcelValue max(int number_of_arguments, ExcelValue *arguments); | |
| 86 86 | 
             
            static ExcelValue min(int number_of_arguments, ExcelValue *arguments);
         | 
| 87 87 | 
             
            static ExcelValue mmult(ExcelValue a_v, ExcelValue b_v);
         | 
| 88 88 | 
             
            static ExcelValue mod(ExcelValue a_v, ExcelValue b_v);
         | 
| 89 | 
            +
            static ExcelValue mround(ExcelValue value_v, ExcelValue multiple_v);
         | 
| 89 90 | 
             
            static ExcelValue na();
         | 
| 90 91 | 
             
            static ExcelValue negative(ExcelValue a_v);
         | 
| 91 92 | 
             
            static ExcelValue excel_not(ExcelValue a_v);
         | 
| @@ -107,6 +108,9 @@ static ExcelValue rounddown(ExcelValue number_v, ExcelValue decimal_places_v); | |
| 107 108 | 
             
            static ExcelValue roundup(ExcelValue number_v, ExcelValue decimal_places_v);
         | 
| 108 109 | 
             
            static ExcelValue excel_int(ExcelValue number_v);
         | 
| 109 110 | 
             
            static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments);
         | 
| 111 | 
            +
            static ExcelValue substitute_3(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v);
         | 
| 112 | 
            +
            static ExcelValue substitute_4(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v, ExcelValue occurrence_number_v);
         | 
| 113 | 
            +
            static ExcelValue substitute(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v, int occurrence);
         | 
| 110 114 | 
             
            static ExcelValue subtotal(ExcelValue type, int number_of_arguments, ExcelValue *arguments);
         | 
| 111 115 | 
             
            static ExcelValue sumifs(ExcelValue sum_range_v, int number_of_arguments, ExcelValue *arguments);
         | 
| 112 116 | 
             
            static ExcelValue sumif(ExcelValue check_range_v, ExcelValue criteria_v, ExcelValue sum_range_v );
         | 
| @@ -305,7 +309,50 @@ static double number_from(ExcelValue v) { | |
| 305 309 | 
             
              return 0;
         | 
| 306 310 | 
             
            }
         | 
| 307 311 |  | 
| 308 | 
            -
             | 
| 312 | 
            +
            static char* string_from(ExcelValue v) {
         | 
| 313 | 
            +
            		char *string;
         | 
| 314 | 
            +
            		switch (v.type) {
         | 
| 315 | 
            +
              	  case ExcelString:
         | 
| 316 | 
            +
            	  		return v.string;
         | 
| 317 | 
            +
            	  		break;
         | 
| 318 | 
            +
              	  case ExcelNumber:
         | 
| 319 | 
            +
            		    string = malloc(20);
         | 
| 320 | 
            +
            		  	if(string == 0) {
         | 
| 321 | 
            +
            		  	  printf("Out of memory in string_from");
         | 
| 322 | 
            +
            		  	  exit(-1);
         | 
| 323 | 
            +
            		  	}
         | 
| 324 | 
            +
            			  snprintf(string,20,"%g",v.number);
         | 
| 325 | 
            +
                    free_later(string);
         | 
| 326 | 
            +
                    return string;
         | 
| 327 | 
            +
            			  break;
         | 
| 328 | 
            +
            		  case ExcelBoolean:
         | 
| 329 | 
            +
            		  	if(v.number == true) {
         | 
| 330 | 
            +
            		  		string = "TRUE";
         | 
| 331 | 
            +
                      free_later(string);
         | 
| 332 | 
            +
                      return string;
         | 
| 333 | 
            +
              			} else {
         | 
| 334 | 
            +
            		  		string = "FALSE";
         | 
| 335 | 
            +
                      free_later(string);
         | 
| 336 | 
            +
                      return string;
         | 
| 337 | 
            +
              			}
         | 
| 338 | 
            +
                    break;
         | 
| 339 | 
            +
            		  case ExcelEmpty:
         | 
| 340 | 
            +
            		  	string = "";
         | 
| 341 | 
            +
                    free_later(string);
         | 
| 342 | 
            +
                    break;
         | 
| 343 | 
            +
                  case ExcelError:
         | 
| 344 | 
            +
                    conversion_error = 1;
         | 
| 345 | 
            +
                    return 0;
         | 
| 346 | 
            +
            	  	case ExcelRange:
         | 
| 347 | 
            +
                    conversion_error = 1;
         | 
| 348 | 
            +
                    return 0;
         | 
| 349 | 
            +
            		}
         | 
| 350 | 
            +
                conversion_error = 1;
         | 
| 351 | 
            +
                return 0;
         | 
| 352 | 
            +
            }
         | 
| 353 | 
            +
             | 
| 354 | 
            +
            #define NUMBER(value_name, name) double name; if(value_name.type == ExcelError) { conversion_error = 0; return value_name; }; name = number_from(value_name);
         | 
| 355 | 
            +
            #define STRING(value_name, name) char *name; if(value_name.type == ExcelError) { conversion_error = 0; return value_name; }; name = string_from(value_name);
         | 
| 309 356 | 
             
            #define CHECK_FOR_CONVERSION_ERROR 	if(conversion_error) { conversion_error = 0; return VALUE; };
         | 
| 310 357 | 
             
            #define CHECK_FOR_PASSED_ERROR(name) 	if(name.type == ExcelError) return name;
         | 
| 311 358 |  | 
| @@ -465,6 +512,7 @@ static ExcelValue rate(ExcelValue periods_v, ExcelValue payment_v, ExcelValue pr | |
| 465 512 | 
             
              NUMBER(payment_v, payment)
         | 
| 466 513 | 
             
              NUMBER(presentValue_v, presentValue)
         | 
| 467 514 | 
             
              NUMBER(finalValue_v, finalValue)
         | 
| 515 | 
            +
              CHECK_FOR_CONVERSION_ERROR;
         | 
| 468 516 |  | 
| 469 517 | 
             
              // FIXME: Only implemented the case where payment is zero
         | 
| 470 518 | 
             
              if(payment != 0) {
         | 
| @@ -474,6 +522,29 @@ static ExcelValue rate(ExcelValue periods_v, ExcelValue payment_v, ExcelValue pr | |
| 474 522 | 
             
              return EXCEL_NUMBER(pow((finalValue/(-presentValue)),(1.0/periods))-1.0);
         | 
| 475 523 | 
             
            }
         | 
| 476 524 |  | 
| 525 | 
            +
            static ExcelValue mround(ExcelValue value_v, ExcelValue multiple_v) {
         | 
| 526 | 
            +
              CHECK_FOR_PASSED_ERROR(value_v)
         | 
| 527 | 
            +
              CHECK_FOR_PASSED_ERROR(multiple_v)
         | 
| 528 | 
            +
             | 
| 529 | 
            +
              NUMBER(value_v, value)
         | 
| 530 | 
            +
              NUMBER(multiple_v, multiple)
         | 
| 531 | 
            +
              CHECK_FOR_CONVERSION_ERROR;
         | 
| 532 | 
            +
             | 
| 533 | 
            +
              if( (value < 0) != (multiple < 0)) {
         | 
| 534 | 
            +
                return NUM;
         | 
| 535 | 
            +
              }
         | 
| 536 | 
            +
             | 
| 537 | 
            +
              if(value == 0) {
         | 
| 538 | 
            +
                return ZERO;
         | 
| 539 | 
            +
              }
         | 
| 540 | 
            +
             | 
| 541 | 
            +
              if(multiple == 0) {
         | 
| 542 | 
            +
                return ZERO;
         | 
| 543 | 
            +
              }
         | 
| 544 | 
            +
             | 
| 545 | 
            +
              return EXCEL_NUMBER(round(value / multiple) * multiple);
         | 
| 546 | 
            +
            }
         | 
| 547 | 
            +
             | 
| 477 548 | 
             
            static ExcelValue excel_and(int array_size, ExcelValue *array) {
         | 
| 478 549 | 
             
            	int i;
         | 
| 479 550 | 
             
            	ExcelValue current_excel_value, array_result;
         | 
| @@ -1777,12 +1848,11 @@ static ExcelValue rank(ExcelValue number_v, ExcelValue range_v, ExcelValue order | |
| 1777 1848 |  | 
| 1778 1849 | 
             
              NUMBER(number_v, number)
         | 
| 1779 1850 | 
             
              NUMBER(order_v, order)
         | 
| 1851 | 
            +
            	CHECK_FOR_CONVERSION_ERROR
         | 
| 1780 1852 |  | 
| 1781 1853 | 
             
              ExcelValue *array;
         | 
| 1782 1854 | 
             
              int size;
         | 
| 1783 1855 |  | 
| 1784 | 
            -
            	CHECK_FOR_CONVERSION_ERROR
         | 
| 1785 | 
            -
             | 
| 1786 1856 | 
             
              if(range_v.type != ExcelRange) {
         | 
| 1787 1857 | 
             
                array = new_excel_value_array(1);
         | 
| 1788 1858 | 
             
                array[0] = range_v;
         | 
| @@ -1934,6 +2004,129 @@ static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments) { | |
| 1934 2004 | 
             
            	return EXCEL_STRING(string);
         | 
| 1935 2005 | 
             
            }
         | 
| 1936 2006 |  | 
| 2007 | 
            +
            static ExcelValue substitute_3(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v) {
         | 
| 2008 | 
            +
              // -1 means all occurrences
         | 
| 2009 | 
            +
              return substitute(string_v, old_string_v, new_string_v, -1);
         | 
| 2010 | 
            +
            }
         | 
| 2011 | 
            +
             | 
| 2012 | 
            +
            static ExcelValue substitute_4(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v, ExcelValue occurrence_number_v) {
         | 
| 2013 | 
            +
              CHECK_FOR_PASSED_ERROR(occurrence_number_v)
         | 
| 2014 | 
            +
              NUMBER(occurrence_number_v,occurrence_number)
         | 
| 2015 | 
            +
              CHECK_FOR_CONVERSION_ERROR
         | 
| 2016 | 
            +
              return substitute(string_v, old_string_v, new_string_v, occurrence_number);
         | 
| 2017 | 
            +
            }
         | 
| 2018 | 
            +
             | 
| 2019 | 
            +
            // Pass < 0 to occurrence_n to replace all occurrences
         | 
| 2020 | 
            +
            static ExcelValue substitute(ExcelValue string_v, ExcelValue old_string_v, ExcelValue new_string_v, int occurrence_n) {
         | 
| 2021 | 
            +
              STRING(string_v, original)
         | 
| 2022 | 
            +
              STRING(old_string_v, from_string)
         | 
| 2023 | 
            +
              STRING(new_string_v, to_string)
         | 
| 2024 | 
            +
              CHECK_FOR_CONVERSION_ERROR
         | 
| 2025 | 
            +
             | 
| 2026 | 
            +
              char *new_string = 0; // Allocated below
         | 
| 2027 | 
            +
              int original_length = strlen(original);
         | 
| 2028 | 
            +
              int from_string_length = strlen(from_string);
         | 
| 2029 | 
            +
              int to_string_length = strlen(to_string);
         | 
| 2030 | 
            +
              int extra_space_per_replacement = (from_string_length < to_string_length) ? (to_string_length - from_string_length) : 0;
         | 
| 2031 | 
            +
             | 
| 2032 | 
            +
              if(from_string_length == 0) {
         | 
| 2033 | 
            +
                return string_v;
         | 
| 2034 | 
            +
              }
         | 
| 2035 | 
            +
             | 
| 2036 | 
            +
              int allocated_length = 0; // Adjusted below
         | 
| 2037 | 
            +
              int space_for_number_of_replacements = 0; // Adjusted below
         | 
| 2038 | 
            +
              int replacement_made = 0;
         | 
| 2039 | 
            +
              int number_of_matches = 0;
         | 
| 2040 | 
            +
              int insertion_point_offset = 0;
         | 
| 2041 | 
            +
              char *insertion_point = 0;
         | 
| 2042 | 
            +
              char *from_point = original;
         | 
| 2043 | 
            +
              char *match_point = 0;
         | 
| 2044 | 
            +
             | 
| 2045 | 
            +
              if(extra_space_per_replacement > 0) {
         | 
| 2046 | 
            +
                space_for_number_of_replacements = 5;
         | 
| 2047 | 
            +
                // Arbitrarily assume 5 replacements as a starting point, plus one for terminator
         | 
| 2048 | 
            +
                allocated_length = original_length + (space_for_number_of_replacements * extra_space_per_replacement) + 1;
         | 
| 2049 | 
            +
              } else {
         | 
| 2050 | 
            +
                // Should be shorter or the same length
         | 
| 2051 | 
            +
                allocated_length = original_length + 1;
         | 
| 2052 | 
            +
              }
         | 
| 2053 | 
            +
             | 
| 2054 | 
            +
              new_string = malloc(allocated_length);
         | 
| 2055 | 
            +
              insertion_point = new_string;
         | 
| 2056 | 
            +
             | 
| 2057 | 
            +
            	if(new_string == 0) {
         | 
| 2058 | 
            +
            	  printf("Out of memory in substitute");
         | 
| 2059 | 
            +
            	  exit(-1);
         | 
| 2060 | 
            +
            	}
         | 
| 2061 | 
            +
             | 
| 2062 | 
            +
              while((from_point-original) < original_length) {
         | 
| 2063 | 
            +
                match_point = strstr(from_point, from_string);
         | 
| 2064 | 
            +
             | 
| 2065 | 
            +
                // No match found
         | 
| 2066 | 
            +
                if(match_point == NULL) {
         | 
| 2067 | 
            +
                  // No match ever found? return the original
         | 
| 2068 | 
            +
                  if(number_of_matches == 0) {
         | 
| 2069 | 
            +
                    break; 
         | 
| 2070 | 
            +
                  }
         | 
| 2071 | 
            +
                  // Copy the remaining string into the target. We should always have space.
         | 
| 2072 | 
            +
                  replacement_made = 1;
         | 
| 2073 | 
            +
                  strcpy(insertion_point, from_point);
         | 
| 2074 | 
            +
                  break;
         | 
| 2075 | 
            +
                }
         | 
| 2076 | 
            +
             | 
| 2077 | 
            +
                number_of_matches = number_of_matches + 1;
         | 
| 2078 | 
            +
             | 
| 2079 | 
            +
                // We may only want to replace a single occurrence
         | 
| 2080 | 
            +
                if(occurrence_n > 0 && occurrence_n != number_of_matches) {
         | 
| 2081 | 
            +
                  // Copy the bit before
         | 
| 2082 | 
            +
                  memcpy(insertion_point, from_point, match_point - from_point);
         | 
| 2083 | 
            +
                  insertion_point = insertion_point + (match_point - from_point);
         | 
| 2084 | 
            +
             | 
| 2085 | 
            +
                  // Copy the original
         | 
| 2086 | 
            +
                  memcpy(insertion_point, from_string, from_string_length);
         | 
| 2087 | 
            +
                  insertion_point = insertion_point + from_string_length;
         | 
| 2088 | 
            +
             | 
| 2089 | 
            +
                  from_point = match_point + from_string_length;
         | 
| 2090 | 
            +
                  continue;
         | 
| 2091 | 
            +
                }
         | 
| 2092 | 
            +
             | 
| 2093 | 
            +
                // We want to replace this occurrence
         | 
| 2094 | 
            +
             | 
| 2095 | 
            +
                // Check we hvae enough space for the replacement
         | 
| 2096 | 
            +
                if(extra_space_per_replacement > 0 && (number_of_matches > space_for_number_of_replacements)) {
         | 
| 2097 | 
            +
                  space_for_number_of_replacements = space_for_number_of_replacements * 2;
         | 
| 2098 | 
            +
                  allocated_length = original_length + (space_for_number_of_replacements * extra_space_per_replacement) + 1;
         | 
| 2099 | 
            +
                  insertion_point_offset = insertion_point - new_string;
         | 
| 2100 | 
            +
                  new_string = realloc(new_string,allocated_length);
         | 
| 2101 | 
            +
                  insertion_point = new_string + insertion_point_offset;
         | 
| 2102 | 
            +
                  if(!new_string) {
         | 
| 2103 | 
            +
                    printf("Out of memory in string substitute realloc trying to increase length to %d", allocated_length);
         | 
| 2104 | 
            +
                    exit(-1);
         | 
| 2105 | 
            +
                  }
         | 
| 2106 | 
            +
                }
         | 
| 2107 | 
            +
             | 
| 2108 | 
            +
                replacement_made = 1;
         | 
| 2109 | 
            +
             | 
| 2110 | 
            +
                // Copy up to the match
         | 
| 2111 | 
            +
                memcpy(insertion_point, from_point, match_point - from_point);
         | 
| 2112 | 
            +
                insertion_point = insertion_point + (match_point - from_point);
         | 
| 2113 | 
            +
             | 
| 2114 | 
            +
                // Copy the replacement
         | 
| 2115 | 
            +
                memcpy(insertion_point, to_string, to_string_length);
         | 
| 2116 | 
            +
                insertion_point = insertion_point + to_string_length;
         | 
| 2117 | 
            +
             | 
| 2118 | 
            +
                from_point = match_point + from_string_length;
         | 
| 2119 | 
            +
              }
         | 
| 2120 | 
            +
             | 
| 2121 | 
            +
              if(replacement_made == 1) {
         | 
| 2122 | 
            +
                free_later(new_string);
         | 
| 2123 | 
            +
                return EXCEL_STRING(new_string);
         | 
| 2124 | 
            +
              } else {
         | 
| 2125 | 
            +
                free(new_string);
         | 
| 2126 | 
            +
                return string_v;
         | 
| 2127 | 
            +
              }
         | 
| 2128 | 
            +
            }
         | 
| 2129 | 
            +
             | 
| 1937 2130 | 
             
            static ExcelValue subtotal(ExcelValue subtotal_type_v, int number_of_arguments, ExcelValue *arguments) {
         | 
| 1938 2131 | 
             
              CHECK_FOR_PASSED_ERROR(subtotal_type_v)
         | 
| 1939 2132 | 
             
              NUMBER(subtotal_type_v,subtotal_type)
         | 
| @@ -2022,18 +2215,30 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume | |
| 2022 2215 |  | 
| 2023 2216 | 
             
                if(current_value.type == ExcelString) {
         | 
| 2024 2217 | 
             
                  s = current_value.string;
         | 
| 2218 | 
            +
                  while(s[0] == ' ') {
         | 
| 2219 | 
            +
                    s = s + 1;
         | 
| 2220 | 
            +
                  }
         | 
| 2025 2221 | 
             
                  if(s[0] == '<') {
         | 
| 2026 2222 | 
             
                    if( s[1] == '>') {
         | 
| 2223 | 
            +
                      while(s[2] == ' ') {
         | 
| 2224 | 
            +
                        s = s + 1;
         | 
| 2225 | 
            +
                      }
         | 
| 2027 2226 | 
             
                      new_comparator = strndup(s+2,strlen(s)-2);
         | 
| 2028 2227 | 
             
                      free_later(new_comparator);
         | 
| 2029 2228 | 
             
                      criteria[i].type = NotEqual;
         | 
| 2030 2229 | 
             
                      criteria[i].comparator = EXCEL_STRING(new_comparator);
         | 
| 2031 2230 | 
             
                    } else if(s[1] == '=') {
         | 
| 2231 | 
            +
                      while(s[2] == ' ') {
         | 
| 2232 | 
            +
                        s = s + 1;
         | 
| 2233 | 
            +
                      }
         | 
| 2032 2234 | 
             
                      new_comparator = strndup(s+2,strlen(s)-2);
         | 
| 2033 2235 | 
             
                      free_later(new_comparator);
         | 
| 2034 2236 | 
             
                      criteria[i].type = LessThanOrEqual;
         | 
| 2035 2237 | 
             
                      criteria[i].comparator = EXCEL_STRING(new_comparator);
         | 
| 2036 2238 | 
             
                    } else {
         | 
| 2239 | 
            +
                      while(s[1] == ' ') {
         | 
| 2240 | 
            +
                        s = s + 1;
         | 
| 2241 | 
            +
                      }
         | 
| 2037 2242 | 
             
                      new_comparator = strndup(s+1,strlen(s)-1);
         | 
| 2038 2243 | 
             
                      free_later(new_comparator);
         | 
| 2039 2244 | 
             
                      criteria[i].type = LessThan;
         | 
| @@ -2041,17 +2246,26 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume | |
| 2041 2246 | 
             
                    }
         | 
| 2042 2247 | 
             
                  } else if(s[0] == '>') {
         | 
| 2043 2248 | 
             
                    if(s[1] == '=') {
         | 
| 2249 | 
            +
                      while(s[2] == ' ') {
         | 
| 2250 | 
            +
                        s = s + 1;
         | 
| 2251 | 
            +
                      }
         | 
| 2044 2252 | 
             
                      new_comparator = strndup(s+2,strlen(s)-2);
         | 
| 2045 2253 | 
             
                      free_later(new_comparator);
         | 
| 2046 2254 | 
             
                      criteria[i].type = MoreThanOrEqual;
         | 
| 2047 2255 | 
             
                      criteria[i].comparator = EXCEL_STRING(new_comparator);
         | 
| 2048 2256 | 
             
                    } else {
         | 
| 2257 | 
            +
                      while(s[1] == ' ') {
         | 
| 2258 | 
            +
                        s = s + 1;
         | 
| 2259 | 
            +
                      }
         | 
| 2049 2260 | 
             
                      new_comparator = strndup(s+1,strlen(s)-1);
         | 
| 2050 2261 | 
             
                      free_later(new_comparator);
         | 
| 2051 2262 | 
             
                      criteria[i].type = MoreThan;
         | 
| 2052 2263 | 
             
                      criteria[i].comparator = EXCEL_STRING(new_comparator);
         | 
| 2053 2264 | 
             
                    }
         | 
| 2054 2265 | 
             
                  } else if(s[0] == '=') {
         | 
| 2266 | 
            +
                    while(s[1] == ' ') {
         | 
| 2267 | 
            +
                      s = s + 1;
         | 
| 2268 | 
            +
                    }
         | 
| 2055 2269 | 
             
                    new_comparator = strndup(s+1,strlen(s)-1);
         | 
| 2056 2270 | 
             
                    free_later(new_comparator);
         | 
| 2057 2271 | 
             
                    criteria[i].type = Equal;
         | 
| @@ -2273,18 +2487,30 @@ static ExcelValue countifs(int number_of_arguments, ExcelValue *arguments) { | |
| 2273 2487 |  | 
| 2274 2488 | 
             
                if(current_value.type == ExcelString) {
         | 
| 2275 2489 | 
             
                  s = current_value.string;
         | 
| 2490 | 
            +
                  while(s[0] == ' ') {
         | 
| 2491 | 
            +
                    s = s + 1;
         | 
| 2492 | 
            +
                  }
         | 
| 2276 2493 | 
             
                  if(s[0] == '<') {
         | 
| 2277 2494 | 
             
                    if( s[1] == '>') {
         | 
| 2495 | 
            +
                      while(s[2] == ' ') {
         | 
| 2496 | 
            +
                        s = s + 1;
         | 
| 2497 | 
            +
                      }
         | 
| 2278 2498 | 
             
                      new_comparator = strndup(s+2,strlen(s)-2);
         | 
| 2279 2499 | 
             
                      free_later(new_comparator);
         | 
| 2280 2500 | 
             
                      criteria[i].type = NotEqual;
         | 
| 2281 2501 | 
             
                      criteria[i].comparator = EXCEL_STRING(new_comparator);
         | 
| 2282 2502 | 
             
                    } else if(s[1] == '=') {
         | 
| 2503 | 
            +
                      while(s[2] == ' ') {
         | 
| 2504 | 
            +
                        s = s + 1;
         | 
| 2505 | 
            +
                      }
         | 
| 2283 2506 | 
             
                      new_comparator = strndup(s+2,strlen(s)-2);
         | 
| 2284 2507 | 
             
                      free_later(new_comparator);
         | 
| 2285 2508 | 
             
                      criteria[i].type = LessThanOrEqual;
         | 
| 2286 2509 | 
             
                      criteria[i].comparator = EXCEL_STRING(new_comparator);
         | 
| 2287 2510 | 
             
                    } else {
         | 
| 2511 | 
            +
                      while(s[1] == ' ') {
         | 
| 2512 | 
            +
                        s = s + 1;
         | 
| 2513 | 
            +
                      }
         | 
| 2288 2514 | 
             
                      new_comparator = strndup(s+1,strlen(s)-1);
         | 
| 2289 2515 | 
             
                      free_later(new_comparator);
         | 
| 2290 2516 | 
             
                      criteria[i].type = LessThan;
         | 
| @@ -2292,17 +2518,26 @@ static ExcelValue countifs(int number_of_arguments, ExcelValue *arguments) { | |
| 2292 2518 | 
             
                    }
         | 
| 2293 2519 | 
             
                  } else if(s[0] == '>') {
         | 
| 2294 2520 | 
             
                    if(s[1] == '=') {
         | 
| 2521 | 
            +
                      while(s[2] == ' ') {
         | 
| 2522 | 
            +
                        s = s + 1;
         | 
| 2523 | 
            +
                      }
         | 
| 2295 2524 | 
             
                      new_comparator = strndup(s+2,strlen(s)-2);
         | 
| 2296 2525 | 
             
                      free_later(new_comparator);
         | 
| 2297 2526 | 
             
                      criteria[i].type = MoreThanOrEqual;
         | 
| 2298 2527 | 
             
                      criteria[i].comparator = EXCEL_STRING(new_comparator);
         | 
| 2299 2528 | 
             
                    } else {
         | 
| 2529 | 
            +
                      while(s[1] == ' ') {
         | 
| 2530 | 
            +
                        s = s + 1;
         | 
| 2531 | 
            +
                      }
         | 
| 2300 2532 | 
             
                      new_comparator = strndup(s+1,strlen(s)-1);
         | 
| 2301 2533 | 
             
                      free_later(new_comparator);
         | 
| 2302 2534 | 
             
                      criteria[i].type = MoreThan;
         | 
| 2303 2535 | 
             
                      criteria[i].comparator = EXCEL_STRING(new_comparator);
         | 
| 2304 2536 | 
             
                    }
         | 
| 2305 2537 | 
             
                  } else if(s[0] == '=') {
         | 
| 2538 | 
            +
                    while(s[1] == ' ') {
         | 
| 2539 | 
            +
                      s = s + 1;
         | 
| 2540 | 
            +
                    }
         | 
| 2306 2541 | 
             
                    new_comparator = strndup(s+1,strlen(s)-1);
         | 
| 2307 2542 | 
             
                    free_later(new_comparator);
         | 
| 2308 2543 | 
             
                    criteria[i].type = Equal;
         | 
| @@ -2678,6 +2913,8 @@ static ExcelValue text(ExcelValue number_v, ExcelValue format_v) { | |
| 2678 2913 | 
             
                snprintf(s, 99, "%'0.3f",number_v.number);
         | 
| 2679 2914 | 
             
              } else if(strcmp(format_v.string,"0000") == 0) {
         | 
| 2680 2915 | 
             
                snprintf(s, 99, "%04.0f",number_v.number);
         | 
| 2916 | 
            +
              } else if(strcmp(format_v.string,"#,000") == 0) {
         | 
| 2917 | 
            +
                snprintf(s, 99, "%'03.0f",number_v.number);
         | 
| 2681 2918 | 
             
              } else {
         | 
| 2682 2919 | 
             
                snprintf(s, 99, "Text format not recognised");
         | 
| 2683 2920 | 
             
              }
         | 
| @@ -2821,6 +3058,11 @@ static ExcelValue curve_5(ExcelValue curveType, ExcelValue currentYear, ExcelVal | |
| 2821 3058 | 
             
            }
         | 
| 2822 3059 |  | 
| 2823 3060 | 
             
            static ExcelValue scurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
         | 
| 3061 | 
            +
            	CHECK_FOR_PASSED_ERROR(currentYear_v)
         | 
| 3062 | 
            +
            	CHECK_FOR_PASSED_ERROR(startValue_v)
         | 
| 3063 | 
            +
            	CHECK_FOR_PASSED_ERROR(endValue_v)
         | 
| 3064 | 
            +
            	CHECK_FOR_PASSED_ERROR(duration_v)
         | 
| 3065 | 
            +
            	CHECK_FOR_PASSED_ERROR(startYear_v)
         | 
| 2824 3066 |  | 
| 2825 3067 | 
             
            	NUMBER(currentYear_v, currentYear)
         | 
| 2826 3068 | 
             
            	NUMBER(startValue_v, startValue)
         | 
| @@ -2844,6 +3086,11 @@ static ExcelValue scurve(ExcelValue currentYear_v, ExcelValue startValue_v, Exce | |
| 2844 3086 | 
             
            }
         | 
| 2845 3087 |  | 
| 2846 3088 | 
             
            static ExcelValue halfscurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
         | 
| 3089 | 
            +
            	CHECK_FOR_PASSED_ERROR(currentYear_v)
         | 
| 3090 | 
            +
            	CHECK_FOR_PASSED_ERROR(startValue_v)
         | 
| 3091 | 
            +
            	CHECK_FOR_PASSED_ERROR(endValue_v)
         | 
| 3092 | 
            +
            	CHECK_FOR_PASSED_ERROR(duration_v)
         | 
| 3093 | 
            +
            	CHECK_FOR_PASSED_ERROR(startYear_v)
         | 
| 2847 3094 |  | 
| 2848 3095 | 
             
            	NUMBER(currentYear_v, currentYear)
         | 
| 2849 3096 | 
             
            	NUMBER(startValue_v, startValue)
         | 
| @@ -2867,6 +3114,11 @@ static ExcelValue halfscurve(ExcelValue currentYear_v, ExcelValue startValue_v, | |
| 2867 3114 | 
             
            }
         | 
| 2868 3115 |  | 
| 2869 3116 | 
             
            static ExcelValue lcurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
         | 
| 3117 | 
            +
            	CHECK_FOR_PASSED_ERROR(currentYear_v)
         | 
| 3118 | 
            +
            	CHECK_FOR_PASSED_ERROR(startValue_v)
         | 
| 3119 | 
            +
            	CHECK_FOR_PASSED_ERROR(endValue_v)
         | 
| 3120 | 
            +
            	CHECK_FOR_PASSED_ERROR(duration_v)
         | 
| 3121 | 
            +
            	CHECK_FOR_PASSED_ERROR(startYear_v)
         | 
| 2870 3122 |  | 
| 2871 3123 | 
             
            	NUMBER(currentYear_v, currentYear)
         | 
| 2872 3124 | 
             
            	NUMBER(startValue_v, startValue)
         | 
| @@ -585,6 +585,8 @@ int test_functions() { | |
| 585 585 |  | 
| 586 586 | 
             
              ExcelValue sumifs_array_10f[] = { sumifs_array_3_v, EXCEL_STRING(">=3")};
         | 
| 587 587 | 
             
              assert(sumifs(sumifs_array_3_v,2, sumifs_array_10f).number == 17);
         | 
| 588 | 
            +
              ExcelValue sumifs_array_10g[] = { sumifs_array_3_v, EXCEL_STRING(" >= 3")};
         | 
| 589 | 
            +
              assert(sumifs(sumifs_array_3_v,2, sumifs_array_10g).number == 17);
         | 
| 588 590 |  | 
| 589 591 | 
             
              // ... BLANK in check range should match empty strings, BLANK in criteria should match zero
         | 
| 590 592 | 
             
              ExcelValue sumifs_array_11[] = { BLANK, EXCEL_NUMBER(0)};
         | 
| @@ -658,6 +660,9 @@ int test_functions() { | |
| 658 660 | 
             
              ExcelValue countifs_array_10e[] = { countifs_array_3_v, EXCEL_STRING(">3")};
         | 
| 659 661 | 
             
              assert(countifs(2, countifs_array_10e).number == 3.0);
         | 
| 660 662 |  | 
| 663 | 
            +
              ExcelValue countifs_array_10e2[] = { countifs_array_3_v, EXCEL_STRING("> 3")};
         | 
| 664 | 
            +
              assert(countifs(2, countifs_array_10e2).number == 3.0);
         | 
| 665 | 
            +
             | 
| 661 666 | 
             
              ExcelValue countifs_array_10f[] = { countifs_array_3_v, EXCEL_STRING(">=3")};
         | 
| 662 667 | 
             
              assert(countifs(2, countifs_array_10f).number == 4.0);
         | 
| 663 668 |  | 
| @@ -890,6 +895,8 @@ int test_functions() { | |
| 890 895 | 
             
              assert(strcmp(text(EXCEL_NUMBER(123456789.123456), EXCEL_STRING("#,##")).string, "123,456,789") == 0);
         | 
| 891 896 | 
             
              assert(strcmp(text(EXCEL_NUMBER(123456789.123456), EXCEL_STRING("#,##0")).string, "123,456,789") == 0);
         | 
| 892 897 | 
             
              assert(strcmp(text(EXCEL_NUMBER(123456789.123456), EXCEL_STRING("#,##0.0")).string, "123,456,789.1") == 0);
         | 
| 898 | 
            +
              assert(strcmp(text(EXCEL_NUMBER(3.1), EXCEL_STRING("#,000")).string, "003") == 0);
         | 
| 899 | 
            +
              assert(strcmp(text(EXCEL_NUMBER(1000.3), EXCEL_STRING("#,000")).string, "1,000") == 0);
         | 
| 893 900 | 
             
              assert(strcmp(text(EXCEL_NUMBER(123456789.123456), EXCEL_STRING("!#,##0.0")).string, "Text format not recognised") == 0);
         | 
| 894 901 |  | 
| 895 902 | 
             
              // Test LOG
         | 
| @@ -1212,6 +1219,21 @@ int test_functions() { | |
| 1212 1219 | 
             
              assert(excel_ceiling_math(EXCEL_NUMBER(136), EXCEL_NUMBER(10), ZERO).number == 140);
         | 
| 1213 1220 | 
             
              assert(excel_ceiling_math(EXCEL_NUMBER(-136), EXCEL_NUMBER(10), ZERO).number == -130);
         | 
| 1214 1221 | 
             
              assert(excel_ceiling_math(EXCEL_NUMBER(-136), EXCEL_NUMBER(10), ONE).number == -140);
         | 
| 1222 | 
            +
              
         | 
| 1223 | 
            +
              // MROUND
         | 
| 1224 | 
            +
              assert(mround(EXCEL_NUMBER(1), ZERO).number == 0.0);
         | 
| 1225 | 
            +
              assert(mround(ZERO, EXCEL_NUMBER(1)).number == 0.0);
         | 
| 1226 | 
            +
              assert(mround(EXCEL_NUMBER(1), EXCEL_NUMBER(1)).number == 1.0);
         | 
| 1227 | 
            +
              assert(mround(EXCEL_NUMBER(105), EXCEL_NUMBER(10)).number == 110);
         | 
| 1228 | 
            +
              assert(mround(EXCEL_NUMBER(1.05), EXCEL_NUMBER(0.1)).number == 1.10);
         | 
| 1229 | 
            +
              assert(mround(EXCEL_NUMBER(-1.05), EXCEL_NUMBER(-0.1)).number == -1.10);
         | 
| 1230 | 
            +
              assert(mround(EXCEL_NUMBER(-1.05), EXCEL_NUMBER(0.1)).type == ExcelError);
         | 
| 1231 | 
            +
             | 
| 1232 | 
            +
              // SUBSTITUTE 3
         | 
| 1233 | 
            +
              assert_equal(EXCEL_STRING("Hello Bob, Dear Bob"), substitute_3(EXCEL_STRING("Hello Bob, Dear Bob"), EXCEL_STRING("Bill"), EXCEL_STRING("Frank")), "substitute('Hello Bob, Dear Bob', 'Bill', 'Frank') = 'Hello Bob, Dear Bob')");
         | 
| 1234 | 
            +
              assert_equal(EXCEL_STRING("Hello Frank, Dear Frank"), substitute_3(EXCEL_STRING("Hello Bob, Dear Bob"), EXCEL_STRING("Bob"), EXCEL_STRING("Frank")), "substitute('Hello Bob, Dear Bob', 'Bob', 'Frank') = 'Hello Frank, Dear Frank')");
         | 
| 1235 | 
            +
              assert_equal(EXCEL_STRING("Hello Bob, Dear Frank"), substitute_4(EXCEL_STRING("Hello Bob, Dear Bob"), EXCEL_STRING("Bob"), EXCEL_STRING("Frank"), TWO), "substitute('Hello Bob, Dear Bob', 'Bob', 'Frank', 2) = 'Hello Bob, Dear Frank')");
         | 
| 1236 | 
            +
              assert_equal(EXCEL_STRING("Hello Bob, Dear Bob"), substitute_4(EXCEL_STRING("Hello Bob, Dear Bob"), EXCEL_STRING("Bob"), EXCEL_STRING("Frank"), THREE), "substitute('Hello Bob, Dear Bob', 'Bob', 'Frank', 3) = 'Hello Bob, Dear Bob')");
         | 
| 1215 1237 |  | 
| 1216 1238 | 
             
              // curve (a custom climact function)
         | 
| 1217 1239 | 
             
              assert_equal(
         | 
| @@ -1239,6 +1261,20 @@ int test_functions() { | |
| 1239 1261 | 
             
                "curve_5(blank, 2023, 0, 10, 10) == 5"
         | 
| 1240 1262 | 
             
              );
         | 
| 1241 1263 |  | 
| 1264 | 
            +
              // CONVERSION ERROR RESET
         | 
| 1265 | 
            +
              assert(conversion_error == 0);
         | 
| 1266 | 
            +
              assert_equal(
         | 
| 1267 | 
            +
                VALUE,
         | 
| 1268 | 
            +
                curve_5(
         | 
| 1269 | 
            +
                  EXCEL_STRING("s"),
         | 
| 1270 | 
            +
                  EXCEL_STRING("Should be a number"),
         | 
| 1271 | 
            +
                  VALUE,
         | 
| 1272 | 
            +
                  EXCEL_NUMBER(10),
         | 
| 1273 | 
            +
                  EXCEL_NUMBER(10)
         | 
| 1274 | 
            +
                ),
         | 
| 1275 | 
            +
                "A messed up curve call"
         | 
| 1276 | 
            +
              );
         | 
| 1277 | 
            +
              assert(conversion_error == 0);
         | 
| 1242 1278 |  | 
| 1243 1279 | 
             
              // Release memory
         | 
| 1244 1280 | 
             
              free_all_allocated_memory();
         | 
| @@ -1249,6 +1285,8 @@ int test_functions() { | |
| 1249 1285 | 
             
              return 0;
         | 
| 1250 1286 | 
             
            }
         | 
| 1251 1287 |  | 
| 1288 | 
            +
             | 
| 1289 | 
            +
             | 
| 1252 1290 | 
             
            int main() {
         | 
| 1253 1291 | 
             
              return test_functions();
         | 
| 1254 1292 | 
             
            }
         | 
| @@ -75,6 +75,7 @@ class MapFormulaeToC < MapValuesToC | |
| 75 75 | 
             
                :'MIN' => 'min',
         | 
| 76 76 | 
             
                :'MMULT' => 'mmult',
         | 
| 77 77 | 
             
                :'MOD' => 'mod',
         | 
| 78 | 
            +
                :'MROUND' => 'mround',
         | 
| 78 79 | 
             
                :'NA' => 'na',
         | 
| 79 80 | 
             
                :'NOT' => 'excel_not',
         | 
| 80 81 | 
             
                :'OR' => 'excel_or',
         | 
| @@ -97,6 +98,8 @@ class MapFormulaeToC < MapValuesToC | |
| 97 98 | 
             
                :'ROUNDUP' => 'roundup',
         | 
| 98 99 | 
             
                :'string_join' => 'string_join',
         | 
| 99 100 | 
             
                :'SUBTOTAL' => 'subtotal',
         | 
| 101 | 
            +
                :'SUBSTITUTE3' => 'substitute_3',
         | 
| 102 | 
            +
                :'SUBSTITUTE4' => 'substitute_4',
         | 
| 100 103 | 
             
                :'SUM' => 'sum',
         | 
| 101 104 | 
             
                :'SUMIF2' => 'sumif_2',
         | 
| 102 105 | 
             
                :'SUMIF3' => 'sumif',
         | 
| @@ -22,7 +22,6 @@ class MapFormulaeToRuby < MapValuesToRuby | |
| 22 22 | 
             
                :'AVERAGE' => 'average',
         | 
| 23 23 | 
             
                :'AVERAGEIFS' => 'averageifs',
         | 
| 24 24 | 
             
                :'CEILING' => 'ceiling',
         | 
| 25 | 
            -
                :'_xlfn.CEILING.MATH' => 'ceiling',
         | 
| 26 25 | 
             
                :'CELL' => 'cell',
         | 
| 27 26 | 
             
                :'CHAR' => 'char',
         | 
| 28 27 | 
             
                :'CHOOSE' => 'choose',
         | 
| @@ -60,6 +59,7 @@ class MapFormulaeToRuby < MapValuesToRuby | |
| 60 59 | 
             
                :'MIN' => 'min',
         | 
| 61 60 | 
             
                :'MMULT' => 'mmult',
         | 
| 62 61 | 
             
                :'MOD' => 'mod',
         | 
| 62 | 
            +
                :'MROUND' => 'mround',
         | 
| 63 63 | 
             
                :'NA' => 'na',
         | 
| 64 64 | 
             
                :'NOT' => 'excel_not',
         | 
| 65 65 | 
             
                :'NPV' => 'npv',
         | 
| @@ -89,6 +89,7 @@ class MapFormulaeToRuby < MapValuesToRuby | |
| 89 89 | 
             
                :'VALUE' => 'value',
         | 
| 90 90 | 
             
                :'VLOOKUP' => 'vlookup',
         | 
| 91 91 | 
             
                :'^' => 'power',
         | 
| 92 | 
            +
                :'_xlfn.CEILING.MATH' => 'ceiling',
         | 
| 92 93 | 
             
                :'_xlfn.FORECAST.LINEAR' => 'forecast',
         | 
| 93 94 | 
             
                :'curve' => 'curve',
         | 
| 94 95 | 
             
                :'halfscurve' => 'halfscurve',
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            module ExcelFunctions
         | 
| 2 | 
            +
              
         | 
| 3 | 
            +
              def mround(value, multiple)
         | 
| 4 | 
            +
                value = number_argument(value)
         | 
| 5 | 
            +
                multiple = number_argument(multiple)
         | 
| 6 | 
            +
                return value if value.is_a?(Symbol)
         | 
| 7 | 
            +
                return multiple if multiple.is_a?(Symbol)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                # Must both have the same sign
         | 
| 10 | 
            +
                return :num unless (value < 0) == (multiple < 0)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                # Zeros just return zero
         | 
| 13 | 
            +
                return 0 if value == 0
         | 
| 14 | 
            +
                return 0 if multiple == 0
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                (value.to_f / multiple.to_f).round * multiple.to_f
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
              
         | 
| 19 | 
            +
            end
         | 
| @@ -69,7 +69,7 @@ module ExcelFunctions | |
| 69 69 | 
             
                      when Numeric
         | 
| 70 70 | 
             
                        check_value == required_value
         | 
| 71 71 | 
             
                      when String
         | 
| 72 | 
            -
                        required_value =~  | 
| 72 | 
            +
                        required_value =~ /^\s*(<=|>=|<|>)?\s*([-+]?[0-9]+\.?[0-9]*([eE][-+]?[0-9]+)?)\s*$/
         | 
| 73 73 | 
             
                        if $1 && $2
         | 
| 74 74 | 
             
                          check_value.send($1,$2.to_f)
         | 
| 75 75 | 
             
                        elsif $2
         | 
| @@ -20,9 +20,12 @@ module ExcelFunctions | |
| 20 20 | 
             
                  text(number*100, $1)+"%"
         | 
| 21 21 | 
             
                when /^(0+)$/
         | 
| 22 22 | 
             
                  sprintf("%0#{$1.length}.0f", number)
         | 
| 23 | 
            -
                when  | 
| 23 | 
            +
                when /^#[,.]#*(0[.,]0+)?(0*)$/
         | 
| 24 24 | 
             
                  formated_with_decimals = text(number, $1 || "0")
         | 
| 25 25 | 
             
                  parts = formated_with_decimals.split('.')
         | 
| 26 | 
            +
                  if $2 && parts[0].length < $2.length
         | 
| 27 | 
            +
                    parts[0] = ("0"*($2.length - parts[0].length))+parts[0]
         | 
| 28 | 
            +
                  end
         | 
| 26 29 | 
             
                  parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")
         | 
| 27 30 | 
             
                  parts.join('.')
         | 
| 28 31 | 
             
                when /0[.,](0+)/
         | 
    
        data/src/excel/reference.rb
    CHANGED
    
    
| @@ -99,6 +99,10 @@ class AstExpandArrayFormulae | |
| 99 99 | 
             
              def map_match(ast)
         | 
| 100 100 | 
             
                array_map(ast, false, true, false)
         | 
| 101 101 | 
             
              end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
              def map_offset(ast)
         | 
| 104 | 
            +
                array_map(ast, true, false, false, false, false)
         | 
| 105 | 
            +
              end
         | 
| 102 106 |  | 
| 103 107 | 
             
              def map_subtotal(ast)
         | 
| 104 108 | 
             
                array_map ast, false, *Array.new(ast.length-3,true)
         | 
| @@ -34,11 +34,11 @@ class ExtractArrayFormulaForCell | |
| 34 34 | 
             
                ast[@row_offset+1][@column_offset+1] # plus ones to skip tthe [:array,[:row,"cell"]] symbols
         | 
| 35 35 | 
             
              end
         | 
| 36 36 |  | 
| 37 | 
            -
              FUNCTIONS_THAT_CAN_RETURN_ARRAYS = { INDEX: true,  MMULT: true}
         | 
| 37 | 
            +
              FUNCTIONS_THAT_CAN_RETURN_ARRAYS = { INDEX: true,  MMULT: true, OFFSET: true}
         | 
| 38 38 |  | 
| 39 39 | 
             
              def map_function(ast)
         | 
| 40 40 | 
             
                return ast unless FUNCTIONS_THAT_CAN_RETURN_ARRAYS.has_key?(ast[1])
         | 
| 41 | 
            -
                [:function, :INDEX, ast, @fc.map([:number, (@row_offset+1)]), @fc.map([:number, (column_offset+1)])]
         | 
| 41 | 
            +
                [:function, :INDEX, ast.dup, @fc.map([:number, (@row_offset+1)]), @fc.map([:number, (column_offset+1)])]
         | 
| 42 42 | 
             
              end
         | 
| 43 43 |  | 
| 44 44 | 
             
            end
         | 
| @@ -67,8 +67,12 @@ class ReplaceArraysWithSingleCellsAst | |
| 67 67 | 
             
                    # Replacement made in check_match function
         | 
| 68 68 | 
             
                  elsif ast[1] == :INDIRECT && check_indirect(ast)
         | 
| 69 69 | 
             
                    # Replacement made in check function
         | 
| 70 | 
            +
                  elsif ast[1] == :OFFSET && check_offset(ast)
         | 
| 71 | 
            +
                    # Replacement made in check function
         | 
| 70 72 | 
             
                  elsif ast[0] == :function && ast[1] == :INDEX && check_index(ast)
         | 
| 71 73 | 
             
                    # Replacement made in check
         | 
| 74 | 
            +
                  elsif ast[1] == :'_xlfn.SINGLE'
         | 
| 75 | 
            +
                    ast.replace(try_and_convert_array(ast[2]))
         | 
| 72 76 | 
             
                  end
         | 
| 73 77 | 
             
                end
         | 
| 74 78 | 
             
              end
         | 
| @@ -147,6 +151,32 @@ class ReplaceArraysWithSingleCellsAst | |
| 147 151 | 
             
                true
         | 
| 148 152 | 
             
              end
         | 
| 149 153 |  | 
| 154 | 
            +
              def check_offset(ast)
         | 
| 155 | 
            +
                replacement_made = false
         | 
| 156 | 
            +
                #We ALWAYS leave the reference unchanged
         | 
| 157 | 
            +
                #if ast[2] && ast[2].first == :array 
         | 
| 158 | 
            +
                #  replacement_made = true
         | 
| 159 | 
            +
                #  ast[2] = try_and_convert_array(ast[2])
         | 
| 160 | 
            +
                #end
         | 
| 161 | 
            +
                if ast[3] && ast[3].first == :array
         | 
| 162 | 
            +
                  replacement_made = true
         | 
| 163 | 
            +
                  ast[3] = try_and_convert_array(ast[3])
         | 
| 164 | 
            +
                end
         | 
| 165 | 
            +
                if ast[4] && ast[4].first == :array
         | 
| 166 | 
            +
                  replacement_made = true
         | 
| 167 | 
            +
                  ast[4] = try_and_convert_array(ast[4])
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
                if ast[5] && ast[5].first == :array
         | 
| 170 | 
            +
                  replacement_made = true
         | 
| 171 | 
            +
                  ast[5] = try_and_convert_array(ast[5])
         | 
| 172 | 
            +
                end
         | 
| 173 | 
            +
                if ast[6] && ast[6].first == :array
         | 
| 174 | 
            +
                  replacement_made = true
         | 
| 175 | 
            +
                  ast[6] = try_and_convert_array(ast[6])
         | 
| 176 | 
            +
                end
         | 
| 177 | 
            +
                replacement_made
         | 
| 178 | 
            +
              end
         | 
| 179 | 
            +
             | 
| 150 180 | 
             
              def check_sumifs(ast)
         | 
| 151 181 | 
             
                replacement_made = false
         | 
| 152 182 | 
             
                i = 4
         | 
| @@ -16,7 +16,7 @@ class ReplaceCommonElementsInFormulae | |
| 16 16 | 
             
                input
         | 
| 17 17 | 
             
              end
         | 
| 18 18 |  | 
| 19 | 
            -
              VALUES = {:number => true, :string => true, :blank => true, :null => true, :error => true, :boolean_true => true, :boolean_false => true, :sheet_reference => true, :cell => true, :row => true}
         | 
| 19 | 
            +
              VALUES = {:number => true, :string => true, :blank => true, :null => true, :error => true, :boolean_true => true, :boolean_false => true, :sheet_reference => true, :cell => true, :row => true, :comparator => true}
         | 
| 20 20 |  | 
| 21 21 | 
             
              def replace_repeated_formulae(ast)
         | 
| 22 22 | 
             
                return ast unless ast.is_a?(Array)
         | 
| @@ -5,16 +5,31 @@ class NamedReferences | |
| 5 5 | 
             
              def initialize(refs, tables = {})
         | 
| 6 6 | 
             
                @named_references = refs
         | 
| 7 7 | 
             
                @table_data = tables
         | 
| 8 | 
            +
                @deepCopyCache = {}
         | 
| 8 9 | 
             
              end
         | 
| 9 10 |  | 
| 10 11 | 
             
              def reference_for(sheet,named_reference)
         | 
| 11 12 | 
             
                sheet = sheet.downcase
         | 
| 12 13 | 
             
                named_reference = named_reference.downcase.to_sym
         | 
| 13 | 
            -
                @named_references[[sheet, named_reference]] ||
         | 
| 14 | 
            +
                ref = @named_references[[sheet, named_reference]] ||
         | 
| 14 15 | 
             
                @named_references[named_reference] ||
         | 
| 15 16 | 
             
                @table_data[named_reference] ||
         | 
| 16 17 | 
             
                [:error, :"#NAME?"]
         | 
| 18 | 
            +
                return @deepCopyCache[ref] if @deepCopyCache.key?(ref)
         | 
| 19 | 
            +
                copy = deep_copy(ref)
         | 
| 20 | 
            +
                @deepCopyCache[ref] = copy
         | 
| 21 | 
            +
                return copy
         | 
| 17 22 | 
             
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def deep_copy(ast)
         | 
| 25 | 
            +
                return ast if ast.is_a?(Symbol)
         | 
| 26 | 
            +
                return ast if ast.is_a?(Numeric)
         | 
| 27 | 
            +
                return ast.dup unless ast.is_a?(Array)
         | 
| 28 | 
            +
                ast.map do |a|
         | 
| 29 | 
            +
                  deep_copy(a)
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
             | 
| 18 33 |  | 
| 19 34 | 
             
            end
         | 
| 20 35 |  | 
| @@ -27,22 +27,49 @@ class ReplaceOffsetsWithReferencesAst | |
| 27 27 | 
             
                reference = ast[2]
         | 
| 28 28 | 
             
                row_offset = ast[3]
         | 
| 29 29 | 
             
                column_offset = ast[4]
         | 
| 30 | 
            -
                height = ast[5] | 
| 31 | 
            -
                width = ast[6] | 
| 30 | 
            +
                height = ast[5]
         | 
| 31 | 
            +
                width = ast[6]
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                reference = reference.original unless %i{cell sheet_reference array}.include?(reference.first)
         | 
| 34 | 
            +
                
         | 
| 35 | 
            +
                unless height
         | 
| 36 | 
            +
                  if reference.first != :array
         | 
| 37 | 
            +
                    height = [:number, 1.0]
         | 
| 38 | 
            +
                  else
         | 
| 39 | 
            +
                    height = [:number, reference.length - 1]
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                unless width
         | 
| 44 | 
            +
                  if reference.first != :array
         | 
| 45 | 
            +
                    width = [:number, 1.0]
         | 
| 46 | 
            +
                  else
         | 
| 47 | 
            +
                    width = [:number, reference[1].length - 1]
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                if reference.first == :array
         | 
| 52 | 
            +
                  reference = reference[1][1].original
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 32 55 | 
             
                [row_offset, column_offset, height, width].each do |arg|
         | 
| 33 56 | 
             
                   next unless arg.first == :error
         | 
| 34 57 | 
             
                   ast.replace(arg) 
         | 
| 35 58 | 
             
                   return
         | 
| 36 59 | 
             
                end
         | 
| 60 | 
            +
             | 
| 37 61 | 
             
                return unless [row_offset, column_offset, height, width].all? { |a| a.first == :number }
         | 
| 62 | 
            +
             | 
| 38 63 | 
             
                if reference.first == :cell
         | 
| 39 | 
            -
                  ast.replace(offset_cell(reference, row_offset, column_offset, height, width))
         | 
| 64 | 
            +
                  ast.replace(offset_cell(reference, row_offset, column_offset, height, width, nil))
         | 
| 40 65 | 
             
                elsif reference.first == :sheet_reference && reference[2].first == :cell
         | 
| 41 | 
            -
                  ast.replace( | 
| 66 | 
            +
                  ast.replace(offset_cell(reference[2], row_offset, column_offset, height, width, reference[1]))
         | 
| 67 | 
            +
                else
         | 
| 68 | 
            +
                  p "OFFSET reference is #{reference} from #{ast}, so not replacing"
         | 
| 42 69 | 
             
                end
         | 
| 43 70 | 
             
              end
         | 
| 44 71 |  | 
| 45 | 
            -
              def offset_cell(reference, row_offset, column_offset, height, width)
         | 
| 72 | 
            +
              def offset_cell(reference, row_offset, column_offset, height, width, sheet)
         | 
| 46 73 |  | 
| 47 74 | 
             
                reference = reference[1]
         | 
| 48 75 | 
             
                row_offset = row_offset[1].to_i
         | 
| @@ -57,9 +84,14 @@ class ReplaceOffsetsWithReferencesAst | |
| 57 84 | 
             
                start_reference = reference.offset(row_offset.to_i, column_offset.to_i)
         | 
| 58 85 | 
             
                end_reference = reference.offset(row_offset.to_i + height.to_i - 1, column_offset.to_i + width.to_i - 1)
         | 
| 59 86 | 
             
                if start_reference == end_reference
         | 
| 60 | 
            -
                   | 
| 87 | 
            +
                  if sheet 
         | 
| 88 | 
            +
                    return [:sheet_reference, sheet, [:cell, start_reference]]
         | 
| 89 | 
            +
                  else
         | 
| 90 | 
            +
                    return [:cell, start_reference]
         | 
| 91 | 
            +
                  end
         | 
| 61 92 | 
             
                else
         | 
| 62 | 
            -
                   | 
| 93 | 
            +
                  area = Area.for("#{start_reference}:#{end_reference}")
         | 
| 94 | 
            +
                  return area.to_array_literal(sheet)
         | 
| 63 95 | 
             
                end
         | 
| 64 96 | 
             
              end
         | 
| 65 97 |  | 
    
        data/src/util.rb
    CHANGED
    
    | @@ -1,2 +1 @@ | |
| 1 | 
            -
            require_relative "util/not_supported_exception"
         | 
| 2 | 
            -
            require_relative "util/try"
         | 
| 1 | 
            +
            require_relative "util/not_supported_exception"
         | 
    
        data/src/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: excel_to_code
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.3. | 
| 4 | 
            +
              version: 0.3.20
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Thomas Counsell, Green on Black Ltd
         | 
| 8 | 
            -
            autorequire: | 
| 8 | 
            +
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2022-01-31 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rubypeg
         | 
| @@ -174,9 +174,6 @@ files: | |
| 174 174 | 
             
            - src/commands/excel_to_x.rb
         | 
| 175 175 | 
             
            - src/compile.rb
         | 
| 176 176 | 
             
            - src/compile/c.rb
         | 
| 177 | 
            -
            - src/compile/c/a.out
         | 
| 178 | 
            -
            - src/compile/c/a.out.dSYM/Contents/Info.plist
         | 
| 179 | 
            -
            - src/compile/c/a.out.dSYM/Contents/Resources/DWARF/a.out
         | 
| 180 177 | 
             
            - src/compile/c/compile_named_reference_setters.rb
         | 
| 181 178 | 
             
            - src/compile/c/compile_to_c.rb
         | 
| 182 179 | 
             
            - src/compile/c/compile_to_c_header.rb
         | 
| @@ -254,6 +251,7 @@ files: | |
| 254 251 | 
             
            - src/excel/excel_functions/mod.rb
         | 
| 255 252 | 
             
            - src/excel/excel_functions/more_than.rb
         | 
| 256 253 | 
             
            - src/excel/excel_functions/more_than_or_equal.rb
         | 
| 254 | 
            +
            - src/excel/excel_functions/mround.rb
         | 
| 257 255 | 
             
            - src/excel/excel_functions/multiply.rb
         | 
| 258 256 | 
             
            - src/excel/excel_functions/na.rb
         | 
| 259 257 | 
             
            - src/excel/excel_functions/negative.rb
         | 
| @@ -347,13 +345,12 @@ files: | |
| 347 345 | 
             
            - src/simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays.rb
         | 
| 348 346 | 
             
            - src/util.rb
         | 
| 349 347 | 
             
            - src/util/not_supported_exception.rb
         | 
| 350 | 
            -
            - src/util/try.rb
         | 
| 351 348 | 
             
            - src/version.rb
         | 
| 352 349 | 
             
            homepage: http://github.com/tamc/excel_to_code
         | 
| 353 350 | 
             
            licenses:
         | 
| 354 351 | 
             
            - MIT
         | 
| 355 352 | 
             
            metadata: {}
         | 
| 356 | 
            -
            post_install_message: | 
| 353 | 
            +
            post_install_message:
         | 
| 357 354 | 
             
            rdoc_options: []
         | 
| 358 355 | 
             
            require_paths:
         | 
| 359 356 | 
             
            - src
         | 
| @@ -364,13 +361,12 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 364 361 | 
             
                  version: 2.3.0
         | 
| 365 362 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 366 363 | 
             
              requirements:
         | 
| 367 | 
            -
              - - " | 
| 364 | 
            +
              - - ">="
         | 
| 368 365 | 
             
                - !ruby/object:Gem::Version
         | 
| 369 | 
            -
                  version:  | 
| 366 | 
            +
                  version: '0'
         | 
| 370 367 | 
             
            requirements: []
         | 
| 371 | 
            -
             | 
| 372 | 
            -
             | 
| 373 | 
            -
            signing_key: 
         | 
| 368 | 
            +
            rubygems_version: 3.2.22
         | 
| 369 | 
            +
            signing_key:
         | 
| 374 370 | 
             
            specification_version: 4
         | 
| 375 371 | 
             
            summary: Convert many .xlsx and .xlsm files into equivalent Ruby or C code that can
         | 
| 376 372 | 
             
              be executed without Excel
         | 
    
        data/src/compile/c/a.out
    DELETED
    
    | Binary file | 
| @@ -1,20 +0,0 @@ | |
| 1 | 
            -
            <?xml version="1.0" encoding="UTF-8"?>
         | 
| 2 | 
            -
            <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
         | 
| 3 | 
            -
            <plist version="1.0">
         | 
| 4 | 
            -
            	<dict>
         | 
| 5 | 
            -
            		<key>CFBundleDevelopmentRegion</key>
         | 
| 6 | 
            -
            		<string>English</string>
         | 
| 7 | 
            -
            		<key>CFBundleIdentifier</key>
         | 
| 8 | 
            -
            		<string>com.apple.xcode.dsym.a.out</string>
         | 
| 9 | 
            -
            		<key>CFBundleInfoDictionaryVersion</key>
         | 
| 10 | 
            -
            		<string>6.0</string>
         | 
| 11 | 
            -
            		<key>CFBundlePackageType</key>
         | 
| 12 | 
            -
            		<string>dSYM</string>
         | 
| 13 | 
            -
            		<key>CFBundleSignature</key>
         | 
| 14 | 
            -
            		<string>????</string>
         | 
| 15 | 
            -
            		<key>CFBundleShortVersionString</key>
         | 
| 16 | 
            -
            		<string>1.0</string>
         | 
| 17 | 
            -
            		<key>CFBundleVersion</key>
         | 
| 18 | 
            -
            		<string>1</string>
         | 
| 19 | 
            -
            	</dict>
         | 
| 20 | 
            -
            </plist>
         | 
| Binary file |