excel_to_code 0.0.1
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.
- data/README +41 -0
- data/bin/excel_to_c +63 -0
- data/bin/excel_to_ruby +9 -0
- data/src/commands.rb +2 -0
- data/src/commands/excel_to_c.rb +858 -0
- data/src/commands/excel_to_ruby.rb +620 -0
- data/src/compile.rb +2 -0
- data/src/compile/c.rb +5 -0
- data/src/compile/c/compile_to_c.rb +62 -0
- data/src/compile/c/compile_to_c_header.rb +26 -0
- data/src/compile/c/compile_to_c_unit_test.rb +42 -0
- data/src/compile/c/excel_to_c_runtime.c +2029 -0
- data/src/compile/c/map_formulae_to_c.rb +184 -0
- data/src/compile/c/map_sheet_names_to_c_names.rb +19 -0
- data/src/compile/c/map_values_to_c.rb +85 -0
- data/src/compile/c/map_values_to_c_structs.rb +37 -0
- data/src/compile/ruby.rb +3 -0
- data/src/compile/ruby/compile_to_ruby.rb +33 -0
- data/src/compile/ruby/compile_to_ruby_unit_test.rb +28 -0
- data/src/compile/ruby/excel_to_ruby_runtime.rb +1 -0
- data/src/compile/ruby/map_formulae_to_ruby.rb +95 -0
- data/src/compile/ruby/map_sheet_names_to_ruby_names.rb +19 -0
- data/src/compile/ruby/map_values_to_ruby.rb +65 -0
- data/src/excel.rb +5 -0
- data/src/excel/area.rb +93 -0
- data/src/excel/excel_functions.rb +84 -0
- data/src/excel/excel_functions/abs.rb +14 -0
- data/src/excel/excel_functions/add.rb +18 -0
- data/src/excel/excel_functions/and.rb +30 -0
- data/src/excel/excel_functions/apply_to_range.rb +17 -0
- data/src/excel/excel_functions/average.rb +12 -0
- data/src/excel/excel_functions/choose.rb +18 -0
- data/src/excel/excel_functions/cosh.rb +9 -0
- data/src/excel/excel_functions/count.rb +9 -0
- data/src/excel/excel_functions/counta.rb +8 -0
- data/src/excel/excel_functions/divide.rb +23 -0
- data/src/excel/excel_functions/excel_equal.rb +20 -0
- data/src/excel/excel_functions/excel_if.rb +8 -0
- data/src/excel/excel_functions/excel_match.rb +51 -0
- data/src/excel/excel_functions/find.rb +39 -0
- data/src/excel/excel_functions/iferror.rb +10 -0
- data/src/excel/excel_functions/index.rb +48 -0
- data/src/excel/excel_functions/left.rb +12 -0
- data/src/excel/excel_functions/less_than.rb +26 -0
- data/src/excel/excel_functions/less_than_or_equal.rb +26 -0
- data/src/excel/excel_functions/max.rb +12 -0
- data/src/excel/excel_functions/min.rb +12 -0
- data/src/excel/excel_functions/mod.rb +15 -0
- data/src/excel/excel_functions/more_than.rb +26 -0
- data/src/excel/excel_functions/more_than_or_equal.rb +26 -0
- data/src/excel/excel_functions/multiply.rb +24 -0
- data/src/excel/excel_functions/negative.rb +12 -0
- data/src/excel/excel_functions/not_equal.rb +19 -0
- data/src/excel/excel_functions/number_argument.rb +30 -0
- data/src/excel/excel_functions/pi.rb +7 -0
- data/src/excel/excel_functions/pmt.rb +16 -0
- data/src/excel/excel_functions/power.rb +18 -0
- data/src/excel/excel_functions/round.rb +13 -0
- data/src/excel/excel_functions/rounddown.rb +14 -0
- data/src/excel/excel_functions/roundup.rb +17 -0
- data/src/excel/excel_functions/string_join.rb +19 -0
- data/src/excel/excel_functions/subtotal.rb +13 -0
- data/src/excel/excel_functions/subtract.rb +18 -0
- data/src/excel/excel_functions/sum.rb +8 -0
- data/src/excel/excel_functions/sumif.rb +7 -0
- data/src/excel/excel_functions/sumifs.rb +74 -0
- data/src/excel/excel_functions/sumproduct.rb +32 -0
- data/src/excel/excel_functions/vlookup.rb +49 -0
- data/src/excel/formula_peg.rb +238 -0
- data/src/excel/formula_peg.txt +45 -0
- data/src/excel/reference.rb +56 -0
- data/src/excel/table.rb +108 -0
- data/src/excel_to_code.rb +7 -0
- data/src/extract.rb +13 -0
- data/src/extract/check_for_unknown_functions.rb +20 -0
- data/src/extract/extract_array_formulae.rb +23 -0
- data/src/extract/extract_formulae.rb +36 -0
- data/src/extract/extract_named_references.rb +38 -0
- data/src/extract/extract_relationships.rb +10 -0
- data/src/extract/extract_shared_formulae.rb +23 -0
- data/src/extract/extract_shared_strings.rb +20 -0
- data/src/extract/extract_simple_formulae.rb +18 -0
- data/src/extract/extract_table.rb +24 -0
- data/src/extract/extract_values.rb +29 -0
- data/src/extract/extract_worksheet_dimensions.rb +11 -0
- data/src/extract/extract_worksheet_names.rb +10 -0
- data/src/extract/extract_worksheet_table_relationships.rb +10 -0
- data/src/extract/simple_extract_from_xml.rb +19 -0
- data/src/rewrite.rb +10 -0
- data/src/rewrite/ast_copy_formula.rb +42 -0
- data/src/rewrite/ast_expand_array_formulae.rb +180 -0
- data/src/rewrite/rewrite_array_formulae.rb +71 -0
- data/src/rewrite/rewrite_array_formulae_to_arrays.rb +18 -0
- data/src/rewrite/rewrite_cell_references_to_include_sheet.rb +56 -0
- data/src/rewrite/rewrite_formulae_to_ast.rb +24 -0
- data/src/rewrite/rewrite_merge_formulae_and_values.rb +18 -0
- data/src/rewrite/rewrite_relationship_id_to_filename.rb +22 -0
- data/src/rewrite/rewrite_shared_formulae.rb +38 -0
- data/src/rewrite/rewrite_values_to_ast.rb +28 -0
- data/src/rewrite/rewrite_whole_row_column_references_to_areas.rb +90 -0
- data/src/rewrite/rewrite_worksheet_names.rb +20 -0
- data/src/simplify.rb +16 -0
- data/src/simplify/count_formula_references.rb +58 -0
- data/src/simplify/identify_dependencies.rb +56 -0
- data/src/simplify/identify_repeated_formula_elements.rb +37 -0
- data/src/simplify/inline_formulae.rb +77 -0
- data/src/simplify/map_formulae_to_values.rb +157 -0
- data/src/simplify/remove_cells.rb +18 -0
- data/src/simplify/replace_arrays_with_single_cells.rb +27 -0
- data/src/simplify/replace_blanks.rb +58 -0
- data/src/simplify/replace_common_elements_in_formulae.rb +19 -0
- data/src/simplify/replace_formulae_with_calculated_values.rb +21 -0
- data/src/simplify/replace_indirects_with_references.rb +44 -0
- data/src/simplify/replace_named_references.rb +82 -0
- data/src/simplify/replace_ranges_with_array_literals.rb +54 -0
- data/src/simplify/replace_shared_strings.rb +49 -0
- data/src/simplify/replace_table_references.rb +71 -0
- data/src/simplify/replace_values_with_constants.rb +47 -0
- data/src/simplify/simplify_arithmetic.rb +54 -0
- data/src/util.rb +2 -0
- data/src/util/not_supported_exception.rb +2 -0
- data/src/util/try.rb +9 -0
- metadata +207 -0
data/src/compile.rb
ADDED
data/src/compile/c.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'map_formulae_to_c'
|
2
|
+
|
3
|
+
class CompileToC
|
4
|
+
|
5
|
+
attr_accessor :settable
|
6
|
+
attr_accessor :gettable
|
7
|
+
attr_accessor :worksheet
|
8
|
+
attr_accessor :variable_set_counter
|
9
|
+
|
10
|
+
def self.rewrite(*args)
|
11
|
+
self.new.rewrite(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def rewrite(input,sheet_names_file,output)
|
15
|
+
self.settable ||= lambda { |ref| false }
|
16
|
+
self.gettable ||= lambda { |ref| true }
|
17
|
+
@variable_set_counter ||= 0
|
18
|
+
mapper = MapFormulaeToC.new
|
19
|
+
mapper.worksheet = worksheet
|
20
|
+
mapper.sheet_names = Hash[sheet_names_file.readlines.map { |line| line.strip.split("\t")}]
|
21
|
+
c_name = mapper.sheet_names[worksheet]
|
22
|
+
input.lines do |line|
|
23
|
+
begin
|
24
|
+
ref, formula = line.split("\t")
|
25
|
+
ast = eval(formula)
|
26
|
+
calculation = mapper.map(ast)
|
27
|
+
name = "#{c_name}_#{ref.downcase}"
|
28
|
+
static_or_not = gettable.call(ref) ? "" : "static "
|
29
|
+
if settable.call(ref)
|
30
|
+
output.puts "ExcelValue #{name}_default() {"
|
31
|
+
mapper.initializers.each do |i|
|
32
|
+
output.puts " #{i}"
|
33
|
+
end
|
34
|
+
output.puts " return #{calculation};"
|
35
|
+
output.puts "}"
|
36
|
+
output.puts "static ExcelValue #{name}_variable;"
|
37
|
+
output.puts "ExcelValue #{name}() { if(variable_set[#{@variable_set_counter}] == 1) { return #{name}_variable; } else { return #{c_name}_#{ref.downcase}_default(); } }"
|
38
|
+
output.puts "void set_#{name}(ExcelValue newValue) { variable_set[#{@variable_set_counter}] = 1; #{name}_variable = newValue; }"
|
39
|
+
else
|
40
|
+
output.puts "#{static_or_not}ExcelValue #{name}() {"
|
41
|
+
output.puts " static ExcelValue result;"
|
42
|
+
output.puts " if(variable_set[#{@variable_set_counter}] == 1) { return result;}"
|
43
|
+
mapper.initializers.each do |i|
|
44
|
+
output.puts " #{i}"
|
45
|
+
end
|
46
|
+
#output.puts " return #{calculation};"
|
47
|
+
output.puts " result = #{calculation};"
|
48
|
+
output.puts " variable_set[#{@variable_set_counter}] = 1;"
|
49
|
+
output.puts " return result;"
|
50
|
+
output.puts "}"
|
51
|
+
end
|
52
|
+
output.puts
|
53
|
+
@variable_set_counter += 1
|
54
|
+
mapper.reset
|
55
|
+
rescue Exception => e
|
56
|
+
puts "Exception at line #{line}"
|
57
|
+
raise
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class CompileToCHeader
|
2
|
+
|
3
|
+
attr_accessor :worksheet
|
4
|
+
attr_accessor :gettable
|
5
|
+
attr_accessor :settable
|
6
|
+
|
7
|
+
def self.rewrite(*args)
|
8
|
+
self.new.rewrite(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def rewrite(input,sheet_names_file,output,defaults = nil)
|
12
|
+
c_name = Hash[sheet_names_file.readlines.map { |line| line.strip.split("\t")}][worksheet]
|
13
|
+
self.gettable ||= lambda { |ref| true }
|
14
|
+
self.settable ||= lambda { |ref| false }
|
15
|
+
input.lines do |line|
|
16
|
+
begin
|
17
|
+
ref, formula = line.split("\t")
|
18
|
+
static_or_not = (gettable.call(ref) || settable.call(ref)) ? "" : "static "
|
19
|
+
output.puts "#{static_or_not}ExcelValue #{c_name}_#{ref.downcase}();"
|
20
|
+
rescue Exception => e
|
21
|
+
puts "Exception at line #{line}"
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class CompileToCUnitTest
|
2
|
+
|
3
|
+
def self.rewrite(*args)
|
4
|
+
self.new.rewrite(*args)
|
5
|
+
end
|
6
|
+
|
7
|
+
def rewrite(input,c_name, refs_to_test, output)
|
8
|
+
input.lines do |line|
|
9
|
+
begin
|
10
|
+
ref, formula = line.split("\t")
|
11
|
+
next unless refs_to_test.include?(ref)
|
12
|
+
output.puts "def test_#{c_name}_#{ref.downcase}"
|
13
|
+
output.puts " r = spreadsheet.#{c_name}_#{ref.downcase}"
|
14
|
+
ast = eval(formula)
|
15
|
+
case ast.first
|
16
|
+
when :number, :percentage
|
17
|
+
output.puts " assert_equal(:ExcelNumber,r[:type])"
|
18
|
+
output.puts " assert_in_epsilon(#{ast.last.to_f.to_s},r[:number])"
|
19
|
+
when :error
|
20
|
+
output.puts " assert_equal(:ExcelError,r[:type])"
|
21
|
+
when :string
|
22
|
+
output.puts " assert_equal(:ExcelString,r[:type])"
|
23
|
+
output.puts " assert_equal(#{ast.last.inspect},r[:string].force_encoding('utf-8'))"
|
24
|
+
when :boolean_true
|
25
|
+
output.puts " assert_equal(:ExcelBoolean,r[:type])"
|
26
|
+
output.puts " assert_equal(1,r[:number])"
|
27
|
+
when :boolean_false
|
28
|
+
output.puts " assert_equal(:ExcelBoolean,r[:type])"
|
29
|
+
output.puts " assert_equal(0,r[:number])"
|
30
|
+
else
|
31
|
+
raise NotSupportedException.new("#{ast} type can't be settable")
|
32
|
+
end
|
33
|
+
output.puts "end"
|
34
|
+
output.puts
|
35
|
+
rescue Exception => e
|
36
|
+
puts "Exception at line #{line}"
|
37
|
+
raise
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,2029 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <assert.h>
|
3
|
+
#include <string.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <ctype.h>
|
6
|
+
#include <math.h>
|
7
|
+
|
8
|
+
// FIXME: Extract a header file
|
9
|
+
|
10
|
+
// I predefine an array of ExcelValues to store calculations
|
11
|
+
// Probably bad practice. At the very least, I should make it
|
12
|
+
// link to the cell reference in some way.
|
13
|
+
#define MAX_EXCEL_VALUE_HEAP_SIZE 1000000
|
14
|
+
|
15
|
+
#define true 1
|
16
|
+
#define false 0
|
17
|
+
|
18
|
+
// These are the various types of excel cell, plus ExcelRange which allows the passing of arrays of cells
|
19
|
+
typedef enum {ExcelEmpty, ExcelNumber, ExcelString, ExcelBoolean, ExcelError, ExcelRange} ExcelType;
|
20
|
+
|
21
|
+
struct excel_value {
|
22
|
+
ExcelType type;
|
23
|
+
|
24
|
+
double number; // Used for numbers and for error types
|
25
|
+
char *string; // Used for strings
|
26
|
+
|
27
|
+
// The following three are used for ranges
|
28
|
+
void *array;
|
29
|
+
int rows;
|
30
|
+
int columns;
|
31
|
+
};
|
32
|
+
|
33
|
+
typedef struct excel_value ExcelValue;
|
34
|
+
|
35
|
+
|
36
|
+
// These are used in the SUMIF and SUMIFS criteria (e.g., when passed a string like "<20")
|
37
|
+
typedef enum {LessThan, LessThanOrEqual, Equal, NotEqual, MoreThanOrEqual, MoreThan} ExcelComparisonType;
|
38
|
+
|
39
|
+
struct excel_comparison {
|
40
|
+
ExcelComparisonType type;
|
41
|
+
ExcelValue comparator;
|
42
|
+
};
|
43
|
+
|
44
|
+
typedef struct excel_comparison ExcelComparison;
|
45
|
+
|
46
|
+
// Headers
|
47
|
+
static ExcelValue more_than(ExcelValue a_v, ExcelValue b_v);
|
48
|
+
static ExcelValue more_than_or_equal(ExcelValue a_v, ExcelValue b_v);
|
49
|
+
static ExcelValue not_equal(ExcelValue a_v, ExcelValue b_v);
|
50
|
+
static ExcelValue less_than(ExcelValue a_v, ExcelValue b_v);
|
51
|
+
static ExcelValue less_than_or_equal(ExcelValue a_v, ExcelValue b_v);
|
52
|
+
static ExcelValue find_2(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v);
|
53
|
+
static ExcelValue find(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v, ExcelValue position_to_start_at_v);
|
54
|
+
static ExcelValue iferror(ExcelValue value, ExcelValue value_if_error);
|
55
|
+
static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, ExcelValue column_number_v);
|
56
|
+
static ExcelValue excel_index_2(ExcelValue array_v, ExcelValue row_number_v);
|
57
|
+
static ExcelValue left(ExcelValue string_v, ExcelValue number_of_characters_v);
|
58
|
+
static ExcelValue left_1(ExcelValue string_v);
|
59
|
+
static ExcelValue max(int number_of_arguments, ExcelValue *arguments);
|
60
|
+
static ExcelValue min(int number_of_arguments, ExcelValue *arguments);
|
61
|
+
static ExcelValue mod(ExcelValue a_v, ExcelValue b_v);
|
62
|
+
static ExcelValue negative(ExcelValue a_v);
|
63
|
+
static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v);
|
64
|
+
static ExcelValue power(ExcelValue a_v, ExcelValue b_v);
|
65
|
+
static ExcelValue excel_round(ExcelValue number_v, ExcelValue decimal_places_v);
|
66
|
+
static ExcelValue rounddown(ExcelValue number_v, ExcelValue decimal_places_v);
|
67
|
+
static ExcelValue roundup(ExcelValue number_v, ExcelValue decimal_places_v);
|
68
|
+
static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments);
|
69
|
+
static ExcelValue subtotal(ExcelValue type, int number_of_arguments, ExcelValue *arguments);
|
70
|
+
static ExcelValue sumifs(ExcelValue sum_range_v, int number_of_arguments, ExcelValue *arguments);
|
71
|
+
static ExcelValue sumif(ExcelValue check_range_v, ExcelValue criteria_v, ExcelValue sum_range_v );
|
72
|
+
static ExcelValue sumif_2(ExcelValue check_range_v, ExcelValue criteria_v);
|
73
|
+
static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments);
|
74
|
+
static ExcelValue vlookup_3(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v);
|
75
|
+
static ExcelValue vlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v, ExcelValue match_type_v);
|
76
|
+
|
77
|
+
// My little heap
|
78
|
+
ExcelValue cells[MAX_EXCEL_VALUE_HEAP_SIZE];
|
79
|
+
int cell_counter = 0;
|
80
|
+
|
81
|
+
#define HEAPCHECK if(cell_counter >= MAX_EXCEL_VALUE_HEAP_SIZE) { printf("Heap exceeded"); exit(-1); }
|
82
|
+
|
83
|
+
// The object initializers
|
84
|
+
static ExcelValue new_excel_number(double number) {
|
85
|
+
cell_counter++;
|
86
|
+
HEAPCHECK
|
87
|
+
ExcelValue new_cell = cells[cell_counter];
|
88
|
+
new_cell.type = ExcelNumber;
|
89
|
+
new_cell.number = number;
|
90
|
+
return new_cell;
|
91
|
+
};
|
92
|
+
|
93
|
+
static ExcelValue new_excel_string(char *string) {
|
94
|
+
cell_counter++;
|
95
|
+
HEAPCHECK
|
96
|
+
ExcelValue new_cell = cells[cell_counter];
|
97
|
+
new_cell.type = ExcelString;
|
98
|
+
new_cell.string = string;
|
99
|
+
return new_cell;
|
100
|
+
};
|
101
|
+
|
102
|
+
static ExcelValue new_excel_range(void *array, int rows, int columns) {
|
103
|
+
cell_counter++;
|
104
|
+
HEAPCHECK
|
105
|
+
ExcelValue new_cell = cells[cell_counter];
|
106
|
+
new_cell.type = ExcelRange;
|
107
|
+
new_cell.array =array;
|
108
|
+
new_cell.rows = rows;
|
109
|
+
new_cell.columns = columns;
|
110
|
+
return new_cell;
|
111
|
+
};
|
112
|
+
|
113
|
+
static void * new_excel_value_array(int size) {
|
114
|
+
ExcelValue *pointer = malloc(sizeof(ExcelValue)*size);
|
115
|
+
if(pointer == 0) {
|
116
|
+
printf("Out of memory\n");
|
117
|
+
exit(-1);
|
118
|
+
}
|
119
|
+
return pointer;
|
120
|
+
};
|
121
|
+
|
122
|
+
// Constants
|
123
|
+
const ExcelValue BLANK = {.type = ExcelEmpty, .number = 0};
|
124
|
+
|
125
|
+
const ExcelValue ZERO = {.type = ExcelNumber, .number = 0};
|
126
|
+
const ExcelValue ONE = {.type = ExcelNumber, .number = 1};
|
127
|
+
const ExcelValue TWO = {.type = ExcelNumber, .number = 2};
|
128
|
+
const ExcelValue THREE = {.type = ExcelNumber, .number = 3};
|
129
|
+
const ExcelValue FOUR = {.type = ExcelNumber, .number = 4};
|
130
|
+
const ExcelValue FIVE = {.type = ExcelNumber, .number = 5};
|
131
|
+
const ExcelValue SIX = {.type = ExcelNumber, .number = 6};
|
132
|
+
const ExcelValue SEVEN = {.type = ExcelNumber, .number = 7};
|
133
|
+
const ExcelValue EIGHT = {.type = ExcelNumber, .number = 8};
|
134
|
+
const ExcelValue NINE = {.type = ExcelNumber, .number = 9};
|
135
|
+
const ExcelValue TEN = {.type = ExcelNumber, .number = 10};
|
136
|
+
|
137
|
+
|
138
|
+
// Booleans
|
139
|
+
const ExcelValue TRUE = {.type = ExcelBoolean, .number = true };
|
140
|
+
const ExcelValue FALSE = {.type = ExcelBoolean, .number = false };
|
141
|
+
|
142
|
+
// Errors
|
143
|
+
const ExcelValue VALUE = {.type = ExcelError, .number = 0};
|
144
|
+
const ExcelValue NAME = {.type = ExcelError, .number = 1};
|
145
|
+
const ExcelValue DIV0 = {.type = ExcelError, .number = 2};
|
146
|
+
const ExcelValue REF = {.type = ExcelError, .number = 3};
|
147
|
+
const ExcelValue NA = {.type = ExcelError, .number = 4};
|
148
|
+
|
149
|
+
// This is the error flag
|
150
|
+
static int conversion_error = 0;
|
151
|
+
|
152
|
+
// Helpful for debugging
|
153
|
+
static void inspect_excel_value(ExcelValue v) {
|
154
|
+
ExcelValue *array;
|
155
|
+
int i, j, k;
|
156
|
+
switch (v.type) {
|
157
|
+
case ExcelNumber:
|
158
|
+
printf("Number: %f\n",v.number);
|
159
|
+
break;
|
160
|
+
case ExcelBoolean:
|
161
|
+
if(v.number == true) {
|
162
|
+
printf("True\n");
|
163
|
+
} else if(v.number == false) {
|
164
|
+
printf("False\n");
|
165
|
+
} else {
|
166
|
+
printf("Boolean with undefined state %f\n",v.number);
|
167
|
+
}
|
168
|
+
break;
|
169
|
+
case ExcelEmpty:
|
170
|
+
if(v.number == 0) {
|
171
|
+
printf("Empty\n");
|
172
|
+
} else {
|
173
|
+
printf("Empty with unexpected state %f\n",v.number);
|
174
|
+
}
|
175
|
+
break;
|
176
|
+
case ExcelRange:
|
177
|
+
printf("Range rows: %d, columns: %d\n",v.rows,v.columns);
|
178
|
+
array = v.array;
|
179
|
+
for(i = 0; i < v.rows; i++) {
|
180
|
+
printf("Row %d:\n",i+1);
|
181
|
+
for(j = 0; j < v.columns; j++ ) {
|
182
|
+
printf("%d ",j+1);
|
183
|
+
k = (i * v.columns) + j;
|
184
|
+
inspect_excel_value(array[k]);
|
185
|
+
}
|
186
|
+
}
|
187
|
+
break;
|
188
|
+
case ExcelString:
|
189
|
+
printf("String: '%s'\n",v.string);
|
190
|
+
break;
|
191
|
+
case ExcelError:
|
192
|
+
printf("Error number %f ",v.number);
|
193
|
+
switch( (int)v.number) {
|
194
|
+
case 0: printf("VALUE\n"); break;
|
195
|
+
case 1: printf("NAME\n"); break;
|
196
|
+
case 2: printf("DIV0\n"); break;
|
197
|
+
case 3: printf("REF\n"); break;
|
198
|
+
case 4: printf("NA\n"); break;
|
199
|
+
}
|
200
|
+
break;
|
201
|
+
default:
|
202
|
+
printf("Type %d not recognised",v.type);
|
203
|
+
};
|
204
|
+
}
|
205
|
+
|
206
|
+
// Extracts numbers from ExcelValues
|
207
|
+
// Excel treats empty cells as zero
|
208
|
+
static double number_from(ExcelValue v) {
|
209
|
+
char *s;
|
210
|
+
char *p;
|
211
|
+
double n;
|
212
|
+
ExcelValue *array;
|
213
|
+
switch (v.type) {
|
214
|
+
case ExcelNumber:
|
215
|
+
case ExcelBoolean:
|
216
|
+
return v.number;
|
217
|
+
case ExcelEmpty:
|
218
|
+
return 0;
|
219
|
+
case ExcelRange:
|
220
|
+
array = v.array;
|
221
|
+
return number_from(array[0]);
|
222
|
+
case ExcelString:
|
223
|
+
s = v.string;
|
224
|
+
if (s == NULL || *s == '\0' || isspace(*s)) {
|
225
|
+
return 0;
|
226
|
+
}
|
227
|
+
n = strtod (s, &p);
|
228
|
+
if(*p == '\0') {
|
229
|
+
return n;
|
230
|
+
}
|
231
|
+
conversion_error = 1;
|
232
|
+
return 0;
|
233
|
+
case ExcelError:
|
234
|
+
return 0;
|
235
|
+
}
|
236
|
+
return 0;
|
237
|
+
}
|
238
|
+
|
239
|
+
#define NUMBER(value_name, name) double name; if(value_name.type == ExcelError) { return value_name; }; name = number_from(value_name);
|
240
|
+
#define CHECK_FOR_CONVERSION_ERROR if(conversion_error) { conversion_error = 0; return VALUE; };
|
241
|
+
#define CHECK_FOR_PASSED_ERROR(name) if(name.type == ExcelError) return name;
|
242
|
+
|
243
|
+
static ExcelValue excel_abs(ExcelValue a_v) {
|
244
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
245
|
+
NUMBER(a_v, a)
|
246
|
+
CHECK_FOR_CONVERSION_ERROR
|
247
|
+
|
248
|
+
if(a >= 0.0 ) {
|
249
|
+
return a_v;
|
250
|
+
} else {
|
251
|
+
return new_excel_number(-a);
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
static ExcelValue add(ExcelValue a_v, ExcelValue b_v) {
|
256
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
257
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
258
|
+
NUMBER(a_v, a)
|
259
|
+
NUMBER(b_v, b)
|
260
|
+
CHECK_FOR_CONVERSION_ERROR
|
261
|
+
return new_excel_number(a + b);
|
262
|
+
}
|
263
|
+
|
264
|
+
static ExcelValue excel_and(int array_size, ExcelValue *array) {
|
265
|
+
int i;
|
266
|
+
ExcelValue current_excel_value, array_result;
|
267
|
+
|
268
|
+
for(i=0;i<array_size;i++) {
|
269
|
+
current_excel_value = array[i];
|
270
|
+
switch (current_excel_value.type) {
|
271
|
+
case ExcelNumber:
|
272
|
+
case ExcelBoolean:
|
273
|
+
if(current_excel_value.number == false) return FALSE;
|
274
|
+
break;
|
275
|
+
case ExcelRange:
|
276
|
+
array_result = excel_and( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
277
|
+
if(array_result.type == ExcelError) return array_result;
|
278
|
+
if(array_result.type == ExcelBoolean && array_result.number == false) return FALSE;
|
279
|
+
break;
|
280
|
+
case ExcelString:
|
281
|
+
case ExcelEmpty:
|
282
|
+
break;
|
283
|
+
case ExcelError:
|
284
|
+
return current_excel_value;
|
285
|
+
break;
|
286
|
+
}
|
287
|
+
}
|
288
|
+
return TRUE;
|
289
|
+
}
|
290
|
+
|
291
|
+
struct average_result {
|
292
|
+
double sum;
|
293
|
+
double count;
|
294
|
+
int has_error;
|
295
|
+
ExcelValue error;
|
296
|
+
};
|
297
|
+
|
298
|
+
static struct average_result calculate_average(int array_size, ExcelValue *array) {
|
299
|
+
double sum = 0;
|
300
|
+
double count = 0;
|
301
|
+
int i;
|
302
|
+
ExcelValue current_excel_value;
|
303
|
+
struct average_result array_result, r;
|
304
|
+
|
305
|
+
for(i=0;i<array_size;i++) {
|
306
|
+
current_excel_value = array[i];
|
307
|
+
switch (current_excel_value.type) {
|
308
|
+
case ExcelNumber:
|
309
|
+
sum += current_excel_value.number;
|
310
|
+
count++;
|
311
|
+
break;
|
312
|
+
case ExcelRange:
|
313
|
+
array_result = calculate_average( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
314
|
+
if(array_result.has_error == true) return array_result;
|
315
|
+
sum += array_result.sum;
|
316
|
+
count += array_result.count;
|
317
|
+
break;
|
318
|
+
case ExcelBoolean:
|
319
|
+
case ExcelString:
|
320
|
+
case ExcelEmpty:
|
321
|
+
break;
|
322
|
+
case ExcelError:
|
323
|
+
r.has_error = true;
|
324
|
+
r.error = current_excel_value;
|
325
|
+
return r;
|
326
|
+
break;
|
327
|
+
}
|
328
|
+
}
|
329
|
+
r.count = count;
|
330
|
+
r.sum = sum;
|
331
|
+
return r;
|
332
|
+
}
|
333
|
+
|
334
|
+
static ExcelValue average(int array_size, ExcelValue *array) {
|
335
|
+
struct average_result r = calculate_average(array_size, array);
|
336
|
+
if(r.has_error == true) return r.error;
|
337
|
+
if(r.count == 0) return DIV0;
|
338
|
+
return new_excel_number(r.sum/r.count);
|
339
|
+
}
|
340
|
+
|
341
|
+
static ExcelValue choose(ExcelValue index_v, int array_size, ExcelValue *array) {
|
342
|
+
CHECK_FOR_PASSED_ERROR(index_v)
|
343
|
+
|
344
|
+
int index = (int) number_from(index_v);
|
345
|
+
CHECK_FOR_CONVERSION_ERROR
|
346
|
+
int i;
|
347
|
+
for(i=0;i<array_size;i++) {
|
348
|
+
if(array[i].type == ExcelError) return array[i];
|
349
|
+
}
|
350
|
+
if(index < 1) return VALUE;
|
351
|
+
if(index > array_size) return VALUE;
|
352
|
+
return array[index-1];
|
353
|
+
}
|
354
|
+
|
355
|
+
static ExcelValue count(int array_size, ExcelValue *array) {
|
356
|
+
int i;
|
357
|
+
int n = 0;
|
358
|
+
ExcelValue current_excel_value;
|
359
|
+
|
360
|
+
for(i=0;i<array_size;i++) {
|
361
|
+
current_excel_value = array[i];
|
362
|
+
switch (current_excel_value.type) {
|
363
|
+
case ExcelNumber:
|
364
|
+
n++;
|
365
|
+
break;
|
366
|
+
case ExcelRange:
|
367
|
+
n += count( current_excel_value.rows * current_excel_value.columns, current_excel_value.array ).number;
|
368
|
+
break;
|
369
|
+
case ExcelBoolean:
|
370
|
+
case ExcelString:
|
371
|
+
case ExcelEmpty:
|
372
|
+
case ExcelError:
|
373
|
+
break;
|
374
|
+
}
|
375
|
+
}
|
376
|
+
return new_excel_number(n);
|
377
|
+
}
|
378
|
+
|
379
|
+
static ExcelValue counta(int array_size, ExcelValue *array) {
|
380
|
+
int i;
|
381
|
+
int n = 0;
|
382
|
+
ExcelValue current_excel_value;
|
383
|
+
|
384
|
+
for(i=0;i<array_size;i++) {
|
385
|
+
current_excel_value = array[i];
|
386
|
+
switch(current_excel_value.type) {
|
387
|
+
case ExcelNumber:
|
388
|
+
case ExcelBoolean:
|
389
|
+
case ExcelString:
|
390
|
+
case ExcelError:
|
391
|
+
n++;
|
392
|
+
break;
|
393
|
+
case ExcelRange:
|
394
|
+
n += counta( current_excel_value.rows * current_excel_value.columns, current_excel_value.array ).number;
|
395
|
+
break;
|
396
|
+
case ExcelEmpty:
|
397
|
+
break;
|
398
|
+
}
|
399
|
+
}
|
400
|
+
return new_excel_number(n);
|
401
|
+
}
|
402
|
+
|
403
|
+
static ExcelValue divide(ExcelValue a_v, ExcelValue b_v) {
|
404
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
405
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
406
|
+
NUMBER(a_v, a)
|
407
|
+
NUMBER(b_v, b)
|
408
|
+
CHECK_FOR_CONVERSION_ERROR
|
409
|
+
if(b == 0) return DIV0;
|
410
|
+
return new_excel_number(a / b);
|
411
|
+
}
|
412
|
+
|
413
|
+
static ExcelValue excel_equal(ExcelValue a_v, ExcelValue b_v) {
|
414
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
415
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
416
|
+
|
417
|
+
if(a_v.type != b_v.type) return FALSE;
|
418
|
+
|
419
|
+
switch (a_v.type) {
|
420
|
+
case ExcelNumber:
|
421
|
+
case ExcelBoolean:
|
422
|
+
case ExcelEmpty:
|
423
|
+
if(a_v.number != b_v.number) return FALSE;
|
424
|
+
return TRUE;
|
425
|
+
case ExcelString:
|
426
|
+
if(strcasecmp(a_v.string,b_v.string) != 0 ) return FALSE;
|
427
|
+
return TRUE;
|
428
|
+
case ExcelError:
|
429
|
+
return a_v;
|
430
|
+
case ExcelRange:
|
431
|
+
return NA;
|
432
|
+
}
|
433
|
+
return FALSE;
|
434
|
+
}
|
435
|
+
|
436
|
+
static ExcelValue not_equal(ExcelValue a_v, ExcelValue b_v) {
|
437
|
+
ExcelValue result = excel_equal(a_v, b_v);
|
438
|
+
if(result.type == ExcelBoolean) {
|
439
|
+
if(result.number == 0) return TRUE;
|
440
|
+
return FALSE;
|
441
|
+
}
|
442
|
+
return result;
|
443
|
+
}
|
444
|
+
|
445
|
+
static ExcelValue excel_if(ExcelValue condition, ExcelValue true_case, ExcelValue false_case ) {
|
446
|
+
CHECK_FOR_PASSED_ERROR(condition)
|
447
|
+
CHECK_FOR_PASSED_ERROR(true_case)
|
448
|
+
CHECK_FOR_PASSED_ERROR(false_case)
|
449
|
+
|
450
|
+
switch (condition.type) {
|
451
|
+
case ExcelBoolean:
|
452
|
+
if(condition.number == true) return true_case;
|
453
|
+
return false_case;
|
454
|
+
case ExcelNumber:
|
455
|
+
if(condition.number == false) return false_case;
|
456
|
+
return true_case;
|
457
|
+
case ExcelEmpty:
|
458
|
+
return false_case;
|
459
|
+
case ExcelString:
|
460
|
+
return VALUE;
|
461
|
+
case ExcelError:
|
462
|
+
return condition;
|
463
|
+
case ExcelRange:
|
464
|
+
return VALUE;
|
465
|
+
}
|
466
|
+
return condition;
|
467
|
+
}
|
468
|
+
|
469
|
+
static ExcelValue excel_if_2(ExcelValue condition, ExcelValue true_case ) {
|
470
|
+
return excel_if( condition, true_case, FALSE );
|
471
|
+
}
|
472
|
+
|
473
|
+
static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, ExcelValue column_number_v) {
|
474
|
+
CHECK_FOR_PASSED_ERROR(array_v)
|
475
|
+
CHECK_FOR_PASSED_ERROR(row_number_v)
|
476
|
+
CHECK_FOR_PASSED_ERROR(column_number_v)
|
477
|
+
|
478
|
+
ExcelValue *array;
|
479
|
+
int rows;
|
480
|
+
int columns;
|
481
|
+
|
482
|
+
NUMBER(row_number_v, row_number)
|
483
|
+
NUMBER(column_number_v, column_number)
|
484
|
+
CHECK_FOR_CONVERSION_ERROR
|
485
|
+
|
486
|
+
if(array_v.type == ExcelRange) {
|
487
|
+
array = array_v.array;
|
488
|
+
rows = array_v.rows;
|
489
|
+
columns = array_v.columns;
|
490
|
+
} else {
|
491
|
+
ExcelValue tmp_array[] = {array_v};
|
492
|
+
array = tmp_array;
|
493
|
+
rows = 1;
|
494
|
+
columns = 1;
|
495
|
+
}
|
496
|
+
|
497
|
+
if(row_number > rows) return REF;
|
498
|
+
if(column_number > columns) return REF;
|
499
|
+
|
500
|
+
if(row_number == 0) { // We need the whole column
|
501
|
+
if(column_number < 1) return REF;
|
502
|
+
ExcelValue *result = (ExcelValue *) new_excel_value_array(rows);
|
503
|
+
int result_index = 0;
|
504
|
+
ExcelValue r;
|
505
|
+
int array_index;
|
506
|
+
int i;
|
507
|
+
for(i = 0; i < rows; i++) {
|
508
|
+
array_index = (i*columns) + column_number - 1;
|
509
|
+
r = array[array_index];
|
510
|
+
if(r.type == ExcelEmpty) {
|
511
|
+
result[result_index] = ZERO;
|
512
|
+
} else {
|
513
|
+
result[result_index] = r;
|
514
|
+
}
|
515
|
+
result_index++;
|
516
|
+
}
|
517
|
+
return new_excel_range(result,rows,1);
|
518
|
+
} else if(column_number == 0 ) { // We need the whole row
|
519
|
+
if(row_number < 1) return REF;
|
520
|
+
ExcelValue *result = (ExcelValue*) new_excel_value_array(columns);
|
521
|
+
ExcelValue r;
|
522
|
+
int row_start = ((row_number-1)*columns);
|
523
|
+
int row_finish = row_start + columns;
|
524
|
+
int result_index = 0;
|
525
|
+
int i;
|
526
|
+
for(i = row_start; i < row_finish; i++) {
|
527
|
+
r = array[i];
|
528
|
+
if(r.type == ExcelEmpty) {
|
529
|
+
result[result_index] = ZERO;
|
530
|
+
} else {
|
531
|
+
result[result_index] = r;
|
532
|
+
}
|
533
|
+
result_index++;
|
534
|
+
}
|
535
|
+
return new_excel_range(result,1,columns);
|
536
|
+
} else { // We need a precise point
|
537
|
+
if(row_number < 1) return REF;
|
538
|
+
if(column_number < 1) return REF;
|
539
|
+
int position = ((row_number - 1) * columns) + column_number - 1;
|
540
|
+
ExcelValue result = array[position];
|
541
|
+
if(result.type == ExcelEmpty) return ZERO;
|
542
|
+
return result;
|
543
|
+
}
|
544
|
+
|
545
|
+
return FALSE;
|
546
|
+
};
|
547
|
+
|
548
|
+
static ExcelValue excel_index_2(ExcelValue array_v, ExcelValue offset) {
|
549
|
+
if(array_v.type == ExcelRange) {
|
550
|
+
if(array_v.rows == 1) {
|
551
|
+
return excel_index(array_v,ONE,offset);
|
552
|
+
} else if (array_v.columns == 1) {
|
553
|
+
return excel_index(array_v,offset,ONE);
|
554
|
+
} else {
|
555
|
+
return REF;
|
556
|
+
}
|
557
|
+
} else if (offset.type == ExcelNumber && offset.number == 1) {
|
558
|
+
return array_v;
|
559
|
+
} else {
|
560
|
+
return REF;
|
561
|
+
}
|
562
|
+
return REF;
|
563
|
+
};
|
564
|
+
|
565
|
+
|
566
|
+
static ExcelValue excel_match(ExcelValue lookup_value, ExcelValue lookup_array, ExcelValue match_type ) {
|
567
|
+
CHECK_FOR_PASSED_ERROR(lookup_value)
|
568
|
+
CHECK_FOR_PASSED_ERROR(lookup_array)
|
569
|
+
CHECK_FOR_PASSED_ERROR(match_type)
|
570
|
+
|
571
|
+
// Blanks are treaked as zeros
|
572
|
+
if(lookup_value.type == ExcelEmpty) lookup_value = ZERO;
|
573
|
+
|
574
|
+
// Setup the array
|
575
|
+
ExcelValue *array;
|
576
|
+
int size;
|
577
|
+
if(lookup_array.type == ExcelRange) {
|
578
|
+
// Check that the range is a row or column rather than an area
|
579
|
+
if((lookup_array.rows == 1) || (lookup_array.columns == 1)) {
|
580
|
+
array = lookup_array.array;
|
581
|
+
size = lookup_array.rows * lookup_array.columns;
|
582
|
+
} else {
|
583
|
+
// return NA error if covers an area.
|
584
|
+
return NA;
|
585
|
+
};
|
586
|
+
} else {
|
587
|
+
// Need to wrap the argument up as an array
|
588
|
+
size = 1;
|
589
|
+
ExcelValue tmp_array[1] = {lookup_array};
|
590
|
+
array = tmp_array;
|
591
|
+
}
|
592
|
+
|
593
|
+
int type = (int) number_from(match_type);
|
594
|
+
CHECK_FOR_CONVERSION_ERROR;
|
595
|
+
|
596
|
+
int i;
|
597
|
+
ExcelValue x;
|
598
|
+
|
599
|
+
switch(type) {
|
600
|
+
case 0:
|
601
|
+
for(i = 0; i < size; i++ ) {
|
602
|
+
x = array[i];
|
603
|
+
if(x.type == ExcelEmpty) x = ZERO;
|
604
|
+
if(excel_equal(lookup_value,x).number == true) return new_excel_number(i+1);
|
605
|
+
}
|
606
|
+
return NA;
|
607
|
+
break;
|
608
|
+
case 1:
|
609
|
+
for(i = 0; i < size; i++ ) {
|
610
|
+
x = array[i];
|
611
|
+
if(x.type == ExcelEmpty) x = ZERO;
|
612
|
+
if(more_than(x,lookup_value).number == true) {
|
613
|
+
if(i==0) return NA;
|
614
|
+
return new_excel_number(i);
|
615
|
+
}
|
616
|
+
}
|
617
|
+
return new_excel_number(size);
|
618
|
+
break;
|
619
|
+
case -1:
|
620
|
+
for(i = 0; i < size; i++ ) {
|
621
|
+
x = array[i];
|
622
|
+
if(x.type == ExcelEmpty) x = ZERO;
|
623
|
+
if(less_than(x,lookup_value).number == true) {
|
624
|
+
if(i==0) return NA;
|
625
|
+
return new_excel_number(i);
|
626
|
+
}
|
627
|
+
}
|
628
|
+
return new_excel_number(size-1);
|
629
|
+
break;
|
630
|
+
}
|
631
|
+
return NA;
|
632
|
+
}
|
633
|
+
|
634
|
+
static ExcelValue excel_match_2(ExcelValue lookup_value, ExcelValue lookup_array ) {
|
635
|
+
return excel_match(lookup_value,lookup_array,new_excel_number(0));
|
636
|
+
}
|
637
|
+
|
638
|
+
static ExcelValue find(ExcelValue find_text_v, ExcelValue within_text_v, ExcelValue start_number_v) {
|
639
|
+
CHECK_FOR_PASSED_ERROR(find_text_v)
|
640
|
+
CHECK_FOR_PASSED_ERROR(within_text_v)
|
641
|
+
CHECK_FOR_PASSED_ERROR(start_number_v)
|
642
|
+
|
643
|
+
char *find_text;
|
644
|
+
char *within_text;
|
645
|
+
char *within_text_offset;
|
646
|
+
char *result;
|
647
|
+
int start_number = number_from(start_number_v);
|
648
|
+
CHECK_FOR_CONVERSION_ERROR
|
649
|
+
|
650
|
+
// Deal with blanks
|
651
|
+
if(within_text_v.type == ExcelString) {
|
652
|
+
within_text = within_text_v.string;
|
653
|
+
} else if( within_text_v.type == ExcelEmpty) {
|
654
|
+
within_text = "";
|
655
|
+
}
|
656
|
+
|
657
|
+
if(find_text_v.type == ExcelString) {
|
658
|
+
find_text = find_text_v.string;
|
659
|
+
} else if( find_text_v.type == ExcelEmpty) {
|
660
|
+
return start_number_v;
|
661
|
+
}
|
662
|
+
|
663
|
+
// Check length
|
664
|
+
if(start_number < 1) return VALUE;
|
665
|
+
if(start_number > strlen(within_text)) return VALUE;
|
666
|
+
|
667
|
+
// Offset our within_text pointer
|
668
|
+
// FIXME: No way this is utf-8 compatible
|
669
|
+
within_text_offset = within_text + (start_number - 1);
|
670
|
+
result = strstr(within_text_offset,find_text);
|
671
|
+
if(result) {
|
672
|
+
return new_excel_number(result - within_text + 1);
|
673
|
+
}
|
674
|
+
return VALUE;
|
675
|
+
}
|
676
|
+
|
677
|
+
static ExcelValue find_2(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v) {
|
678
|
+
return find(string_to_look_for_v, string_to_look_in_v, ONE);
|
679
|
+
};
|
680
|
+
|
681
|
+
static ExcelValue left(ExcelValue string_v, ExcelValue number_of_characters_v) {
|
682
|
+
CHECK_FOR_PASSED_ERROR(string_v)
|
683
|
+
CHECK_FOR_PASSED_ERROR(number_of_characters_v)
|
684
|
+
if(string_v.type == ExcelEmpty) return BLANK;
|
685
|
+
if(number_of_characters_v.type == ExcelEmpty) return BLANK;
|
686
|
+
|
687
|
+
int number_of_characters = (int) number_from(number_of_characters_v);
|
688
|
+
CHECK_FOR_CONVERSION_ERROR
|
689
|
+
|
690
|
+
char *string;
|
691
|
+
switch (string_v.type) {
|
692
|
+
case ExcelString:
|
693
|
+
string = string_v.string;
|
694
|
+
break;
|
695
|
+
case ExcelNumber:
|
696
|
+
string = malloc(20);
|
697
|
+
if(string == 0) {
|
698
|
+
printf("Out of memory");
|
699
|
+
exit(-1);
|
700
|
+
}
|
701
|
+
snprintf(string,20,"%f",string_v.number);
|
702
|
+
break;
|
703
|
+
case ExcelBoolean:
|
704
|
+
if(string_v.number == true) {
|
705
|
+
string = "TRUE";
|
706
|
+
} else {
|
707
|
+
string = "FALSE";
|
708
|
+
}
|
709
|
+
break;
|
710
|
+
case ExcelEmpty:
|
711
|
+
case ExcelError:
|
712
|
+
case ExcelRange:
|
713
|
+
return string_v;
|
714
|
+
}
|
715
|
+
|
716
|
+
char *left_string = malloc(number_of_characters+1);
|
717
|
+
if(left_string == 0) {
|
718
|
+
printf("Out of memory");
|
719
|
+
exit(-1);
|
720
|
+
}
|
721
|
+
memcpy(left_string,string,number_of_characters);
|
722
|
+
left_string[number_of_characters] = '\0';
|
723
|
+
return new_excel_string(left_string);
|
724
|
+
}
|
725
|
+
|
726
|
+
static ExcelValue left_1(ExcelValue string_v) {
|
727
|
+
return left(string_v, ONE);
|
728
|
+
}
|
729
|
+
|
730
|
+
static ExcelValue iferror(ExcelValue value, ExcelValue value_if_error) {
|
731
|
+
if(value.type == ExcelError) return value_if_error;
|
732
|
+
return value;
|
733
|
+
}
|
734
|
+
|
735
|
+
static ExcelValue more_than(ExcelValue a_v, ExcelValue b_v) {
|
736
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
737
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
738
|
+
|
739
|
+
switch (a_v.type) {
|
740
|
+
case ExcelNumber:
|
741
|
+
case ExcelBoolean:
|
742
|
+
case ExcelEmpty:
|
743
|
+
if((b_v.type == ExcelNumber) || (b_v.type == ExcelBoolean) || (b_v.type == ExcelEmpty)) {
|
744
|
+
if(a_v.number <= b_v.number) return FALSE;
|
745
|
+
return TRUE;
|
746
|
+
}
|
747
|
+
return FALSE;
|
748
|
+
case ExcelString:
|
749
|
+
if(b_v.type == ExcelString) {
|
750
|
+
if(strcasecmp(a_v.string,b_v.string) <= 0 ) return FALSE;
|
751
|
+
return TRUE;
|
752
|
+
}
|
753
|
+
return FALSE;
|
754
|
+
case ExcelError:
|
755
|
+
return a_v;
|
756
|
+
case ExcelRange:
|
757
|
+
return NA;
|
758
|
+
}
|
759
|
+
return FALSE;
|
760
|
+
}
|
761
|
+
|
762
|
+
static ExcelValue more_than_or_equal(ExcelValue a_v, ExcelValue b_v) {
|
763
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
764
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
765
|
+
|
766
|
+
switch (a_v.type) {
|
767
|
+
case ExcelNumber:
|
768
|
+
case ExcelBoolean:
|
769
|
+
case ExcelEmpty:
|
770
|
+
if((b_v.type == ExcelNumber) || (b_v.type == ExcelBoolean) || (b_v.type == ExcelEmpty)) {
|
771
|
+
if(a_v.number < b_v.number) return FALSE;
|
772
|
+
return TRUE;
|
773
|
+
}
|
774
|
+
return FALSE;
|
775
|
+
case ExcelString:
|
776
|
+
if(b_v.type == ExcelString) {
|
777
|
+
if(strcasecmp(a_v.string,b_v.string) < 0 ) return FALSE;
|
778
|
+
return TRUE;
|
779
|
+
}
|
780
|
+
return FALSE;
|
781
|
+
case ExcelError:
|
782
|
+
return a_v;
|
783
|
+
case ExcelRange:
|
784
|
+
return NA;
|
785
|
+
}
|
786
|
+
return FALSE;
|
787
|
+
}
|
788
|
+
|
789
|
+
|
790
|
+
static ExcelValue less_than(ExcelValue a_v, ExcelValue b_v) {
|
791
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
792
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
793
|
+
|
794
|
+
switch (a_v.type) {
|
795
|
+
case ExcelNumber:
|
796
|
+
case ExcelBoolean:
|
797
|
+
case ExcelEmpty:
|
798
|
+
if((b_v.type == ExcelNumber) || (b_v.type == ExcelBoolean) || (b_v.type == ExcelEmpty)) {
|
799
|
+
if(a_v.number >= b_v.number) return FALSE;
|
800
|
+
return TRUE;
|
801
|
+
}
|
802
|
+
return FALSE;
|
803
|
+
case ExcelString:
|
804
|
+
if(b_v.type == ExcelString) {
|
805
|
+
if(strcasecmp(a_v.string,b_v.string) >= 0 ) return FALSE;
|
806
|
+
return TRUE;
|
807
|
+
}
|
808
|
+
return FALSE;
|
809
|
+
case ExcelError:
|
810
|
+
return a_v;
|
811
|
+
case ExcelRange:
|
812
|
+
return NA;
|
813
|
+
}
|
814
|
+
return FALSE;
|
815
|
+
}
|
816
|
+
|
817
|
+
static ExcelValue less_than_or_equal(ExcelValue a_v, ExcelValue b_v) {
|
818
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
819
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
820
|
+
|
821
|
+
switch (a_v.type) {
|
822
|
+
case ExcelNumber:
|
823
|
+
case ExcelBoolean:
|
824
|
+
case ExcelEmpty:
|
825
|
+
if((b_v.type == ExcelNumber) || (b_v.type == ExcelBoolean) || (b_v.type == ExcelEmpty)) {
|
826
|
+
if(a_v.number > b_v.number) return FALSE;
|
827
|
+
return TRUE;
|
828
|
+
}
|
829
|
+
return FALSE;
|
830
|
+
case ExcelString:
|
831
|
+
if(b_v.type == ExcelString) {
|
832
|
+
if(strcasecmp(a_v.string,b_v.string) > 0 ) return FALSE;
|
833
|
+
return TRUE;
|
834
|
+
}
|
835
|
+
return FALSE;
|
836
|
+
case ExcelError:
|
837
|
+
return a_v;
|
838
|
+
case ExcelRange:
|
839
|
+
return NA;
|
840
|
+
}
|
841
|
+
return FALSE;
|
842
|
+
}
|
843
|
+
|
844
|
+
static ExcelValue subtract(ExcelValue a_v, ExcelValue b_v) {
|
845
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
846
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
847
|
+
NUMBER(a_v, a)
|
848
|
+
NUMBER(b_v, b)
|
849
|
+
CHECK_FOR_CONVERSION_ERROR
|
850
|
+
return new_excel_number(a - b);
|
851
|
+
}
|
852
|
+
|
853
|
+
static ExcelValue multiply(ExcelValue a_v, ExcelValue b_v) {
|
854
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
855
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
856
|
+
NUMBER(a_v, a)
|
857
|
+
NUMBER(b_v, b)
|
858
|
+
CHECK_FOR_CONVERSION_ERROR
|
859
|
+
return new_excel_number(a * b);
|
860
|
+
}
|
861
|
+
|
862
|
+
static ExcelValue sum(int array_size, ExcelValue *array) {
|
863
|
+
double total = 0;
|
864
|
+
int i;
|
865
|
+
for(i=0;i<array_size;i++) {
|
866
|
+
switch(array[i].type) {
|
867
|
+
case ExcelNumber:
|
868
|
+
total += array[i].number;
|
869
|
+
break;
|
870
|
+
case ExcelRange:
|
871
|
+
total += number_from(sum( array[i].rows * array[i].columns, array[i].array ));
|
872
|
+
break;
|
873
|
+
case ExcelError:
|
874
|
+
return array[i];
|
875
|
+
break;
|
876
|
+
default:
|
877
|
+
break;
|
878
|
+
}
|
879
|
+
}
|
880
|
+
return new_excel_number(total);
|
881
|
+
}
|
882
|
+
|
883
|
+
static ExcelValue max(int number_of_arguments, ExcelValue *arguments) {
|
884
|
+
double biggest_number_found;
|
885
|
+
int any_number_found = 0;
|
886
|
+
int i;
|
887
|
+
ExcelValue current_excel_value;
|
888
|
+
|
889
|
+
for(i=0;i<number_of_arguments;i++) {
|
890
|
+
current_excel_value = arguments[i];
|
891
|
+
if(current_excel_value.type == ExcelNumber) {
|
892
|
+
if(!any_number_found) {
|
893
|
+
any_number_found = 1;
|
894
|
+
biggest_number_found = current_excel_value.number;
|
895
|
+
}
|
896
|
+
if(current_excel_value.number > biggest_number_found) biggest_number_found = current_excel_value.number;
|
897
|
+
} else if(current_excel_value.type == ExcelRange) {
|
898
|
+
current_excel_value = max( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
899
|
+
if(current_excel_value.type == ExcelError) return current_excel_value;
|
900
|
+
if(current_excel_value.type == ExcelNumber)
|
901
|
+
if(!any_number_found) {
|
902
|
+
any_number_found = 1;
|
903
|
+
biggest_number_found = current_excel_value.number;
|
904
|
+
}
|
905
|
+
if(current_excel_value.number > biggest_number_found) biggest_number_found = current_excel_value.number;
|
906
|
+
} else if(current_excel_value.type == ExcelError) {
|
907
|
+
return current_excel_value;
|
908
|
+
}
|
909
|
+
}
|
910
|
+
if(!any_number_found) {
|
911
|
+
any_number_found = 1;
|
912
|
+
biggest_number_found = 0;
|
913
|
+
}
|
914
|
+
return new_excel_number(biggest_number_found);
|
915
|
+
}
|
916
|
+
|
917
|
+
static ExcelValue min(int number_of_arguments, ExcelValue *arguments) {
|
918
|
+
double smallest_number_found = 0;
|
919
|
+
int any_number_found = 0;
|
920
|
+
int i;
|
921
|
+
ExcelValue current_excel_value;
|
922
|
+
|
923
|
+
for(i=0;i<number_of_arguments;i++) {
|
924
|
+
current_excel_value = arguments[i];
|
925
|
+
if(current_excel_value.type == ExcelNumber) {
|
926
|
+
if(!any_number_found) {
|
927
|
+
any_number_found = 1;
|
928
|
+
smallest_number_found = current_excel_value.number;
|
929
|
+
}
|
930
|
+
if(current_excel_value.number < smallest_number_found) smallest_number_found = current_excel_value.number;
|
931
|
+
} else if(current_excel_value.type == ExcelRange) {
|
932
|
+
current_excel_value = min( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
933
|
+
if(current_excel_value.type == ExcelError) return current_excel_value;
|
934
|
+
if(current_excel_value.type == ExcelNumber)
|
935
|
+
if(!any_number_found) {
|
936
|
+
any_number_found = 1;
|
937
|
+
smallest_number_found = current_excel_value.number;
|
938
|
+
}
|
939
|
+
if(current_excel_value.number < smallest_number_found) smallest_number_found = current_excel_value.number;
|
940
|
+
} else if(current_excel_value.type == ExcelError) {
|
941
|
+
return current_excel_value;
|
942
|
+
}
|
943
|
+
}
|
944
|
+
if(!any_number_found) {
|
945
|
+
any_number_found = 1;
|
946
|
+
smallest_number_found = 0;
|
947
|
+
}
|
948
|
+
return new_excel_number(smallest_number_found);
|
949
|
+
}
|
950
|
+
|
951
|
+
static ExcelValue mod(ExcelValue a_v, ExcelValue b_v) {
|
952
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
953
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
954
|
+
|
955
|
+
NUMBER(a_v, a)
|
956
|
+
NUMBER(b_v, b)
|
957
|
+
CHECK_FOR_CONVERSION_ERROR
|
958
|
+
if(b == 0) return DIV0;
|
959
|
+
return new_excel_number(fmod(a,b));
|
960
|
+
}
|
961
|
+
|
962
|
+
static ExcelValue negative(ExcelValue a_v) {
|
963
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
964
|
+
NUMBER(a_v, a)
|
965
|
+
CHECK_FOR_CONVERSION_ERROR
|
966
|
+
return new_excel_number(-a);
|
967
|
+
}
|
968
|
+
|
969
|
+
static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v) {
|
970
|
+
CHECK_FOR_PASSED_ERROR(rate_v)
|
971
|
+
CHECK_FOR_PASSED_ERROR(number_of_periods_v)
|
972
|
+
CHECK_FOR_PASSED_ERROR(present_value_v)
|
973
|
+
|
974
|
+
NUMBER(rate_v,rate)
|
975
|
+
NUMBER(number_of_periods_v,number_of_periods)
|
976
|
+
NUMBER(present_value_v,present_value)
|
977
|
+
CHECK_FOR_CONVERSION_ERROR
|
978
|
+
|
979
|
+
if(rate == 0) return new_excel_number(-(present_value / number_of_periods));
|
980
|
+
return new_excel_number(-present_value*(rate*(pow((1+rate),number_of_periods)))/((pow((1+rate),number_of_periods))-1));
|
981
|
+
}
|
982
|
+
|
983
|
+
static ExcelValue power(ExcelValue a_v, ExcelValue b_v) {
|
984
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
985
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
986
|
+
|
987
|
+
NUMBER(a_v, a)
|
988
|
+
NUMBER(b_v, b)
|
989
|
+
CHECK_FOR_CONVERSION_ERROR
|
990
|
+
return new_excel_number(pow(a,b));
|
991
|
+
}
|
992
|
+
|
993
|
+
static ExcelValue excel_round(ExcelValue number_v, ExcelValue decimal_places_v) {
|
994
|
+
CHECK_FOR_PASSED_ERROR(number_v)
|
995
|
+
CHECK_FOR_PASSED_ERROR(decimal_places_v)
|
996
|
+
|
997
|
+
NUMBER(number_v, number)
|
998
|
+
NUMBER(decimal_places_v, decimal_places)
|
999
|
+
CHECK_FOR_CONVERSION_ERROR
|
1000
|
+
|
1001
|
+
double multiple = pow(10,decimal_places);
|
1002
|
+
|
1003
|
+
return new_excel_number( round(number * multiple) / multiple );
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
static ExcelValue rounddown(ExcelValue number_v, ExcelValue decimal_places_v) {
|
1007
|
+
CHECK_FOR_PASSED_ERROR(number_v)
|
1008
|
+
CHECK_FOR_PASSED_ERROR(decimal_places_v)
|
1009
|
+
|
1010
|
+
NUMBER(number_v, number)
|
1011
|
+
NUMBER(decimal_places_v, decimal_places)
|
1012
|
+
CHECK_FOR_CONVERSION_ERROR
|
1013
|
+
|
1014
|
+
double multiple = pow(10,decimal_places);
|
1015
|
+
|
1016
|
+
return new_excel_number( trunc(number * multiple) / multiple );
|
1017
|
+
}
|
1018
|
+
|
1019
|
+
static ExcelValue roundup(ExcelValue number_v, ExcelValue decimal_places_v) {
|
1020
|
+
CHECK_FOR_PASSED_ERROR(number_v)
|
1021
|
+
CHECK_FOR_PASSED_ERROR(decimal_places_v)
|
1022
|
+
|
1023
|
+
NUMBER(number_v, number)
|
1024
|
+
NUMBER(decimal_places_v, decimal_places)
|
1025
|
+
CHECK_FOR_CONVERSION_ERROR
|
1026
|
+
|
1027
|
+
double multiple = pow(10,decimal_places);
|
1028
|
+
if(number < 0) return new_excel_number( floor(number * multiple) / multiple );
|
1029
|
+
return new_excel_number( ceil(number * multiple) / multiple );
|
1030
|
+
}
|
1031
|
+
|
1032
|
+
static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments) {
|
1033
|
+
int allocated_length = 100;
|
1034
|
+
int used_length = 0;
|
1035
|
+
char *string = malloc(allocated_length);
|
1036
|
+
if(string == 0) {
|
1037
|
+
printf("Out of memory");
|
1038
|
+
exit(-1);
|
1039
|
+
}
|
1040
|
+
char *current_string;
|
1041
|
+
int current_string_length;
|
1042
|
+
ExcelValue current_v;
|
1043
|
+
int i;
|
1044
|
+
for(i=0;i<number_of_arguments;i++) {
|
1045
|
+
current_v = (ExcelValue) arguments[i];
|
1046
|
+
switch (current_v.type) {
|
1047
|
+
case ExcelString:
|
1048
|
+
current_string = current_v.string;
|
1049
|
+
break;
|
1050
|
+
case ExcelNumber:
|
1051
|
+
current_string = malloc(20);
|
1052
|
+
if(current_string == 0) {
|
1053
|
+
printf("Out of memory");
|
1054
|
+
exit(-1);
|
1055
|
+
}
|
1056
|
+
snprintf(current_string,20,"%g",current_v.number);
|
1057
|
+
break;
|
1058
|
+
case ExcelBoolean:
|
1059
|
+
if(current_v.number == true) {
|
1060
|
+
current_string = "TRUE";
|
1061
|
+
} else {
|
1062
|
+
current_string = "FALSE";
|
1063
|
+
}
|
1064
|
+
break;
|
1065
|
+
case ExcelEmpty:
|
1066
|
+
current_string = "";
|
1067
|
+
break;
|
1068
|
+
case ExcelError:
|
1069
|
+
return current_v;
|
1070
|
+
case ExcelRange:
|
1071
|
+
return VALUE;
|
1072
|
+
}
|
1073
|
+
current_string_length = strlen(current_string);
|
1074
|
+
if( (used_length + current_string_length + 1) > allocated_length) {
|
1075
|
+
allocated_length += 100;
|
1076
|
+
string = realloc(string,allocated_length);
|
1077
|
+
}
|
1078
|
+
memcpy(string + used_length, current_string, current_string_length);
|
1079
|
+
used_length = used_length + current_string_length;
|
1080
|
+
}
|
1081
|
+
string = realloc(string,used_length+1);
|
1082
|
+
return new_excel_string(string);
|
1083
|
+
}
|
1084
|
+
|
1085
|
+
static ExcelValue subtotal(ExcelValue subtotal_type_v, int number_of_arguments, ExcelValue *arguments) {
|
1086
|
+
CHECK_FOR_PASSED_ERROR(subtotal_type_v)
|
1087
|
+
NUMBER(subtotal_type_v,subtotal_type)
|
1088
|
+
CHECK_FOR_CONVERSION_ERROR
|
1089
|
+
|
1090
|
+
switch((int) subtotal_type) {
|
1091
|
+
case 1:
|
1092
|
+
case 101:
|
1093
|
+
return average(number_of_arguments,arguments);
|
1094
|
+
break;
|
1095
|
+
case 2:
|
1096
|
+
case 102:
|
1097
|
+
return count(number_of_arguments,arguments);
|
1098
|
+
break;
|
1099
|
+
case 3:
|
1100
|
+
case 103:
|
1101
|
+
return counta(number_of_arguments,arguments);
|
1102
|
+
break;
|
1103
|
+
case 9:
|
1104
|
+
case 109:
|
1105
|
+
return sum(number_of_arguments,arguments);
|
1106
|
+
break;
|
1107
|
+
default:
|
1108
|
+
return VALUE;
|
1109
|
+
break;
|
1110
|
+
}
|
1111
|
+
}
|
1112
|
+
|
1113
|
+
static ExcelValue sumifs(ExcelValue sum_range_v, int number_of_arguments, ExcelValue *arguments) {
|
1114
|
+
// First, set up the sum_range
|
1115
|
+
CHECK_FOR_PASSED_ERROR(sum_range_v);
|
1116
|
+
|
1117
|
+
// Set up the sum range
|
1118
|
+
ExcelValue *sum_range;
|
1119
|
+
int sum_range_rows, sum_range_columns;
|
1120
|
+
|
1121
|
+
if(sum_range_v.type == ExcelRange) {
|
1122
|
+
sum_range = sum_range_v.array;
|
1123
|
+
sum_range_rows = sum_range_v.rows;
|
1124
|
+
sum_range_columns = sum_range_v.columns;
|
1125
|
+
} else {
|
1126
|
+
sum_range = (ExcelValue*) new_excel_value_array(1);
|
1127
|
+
sum_range[0] = sum_range_v;
|
1128
|
+
sum_range_rows = 1;
|
1129
|
+
sum_range_columns = 1;
|
1130
|
+
}
|
1131
|
+
|
1132
|
+
// Then go through and set up the check ranges
|
1133
|
+
if(number_of_arguments % 2 != 0) return VALUE;
|
1134
|
+
int number_of_criteria = number_of_arguments / 2;
|
1135
|
+
ExcelValue *criteria_range = (ExcelValue*) new_excel_value_array(number_of_criteria);
|
1136
|
+
ExcelValue current_value;
|
1137
|
+
int i;
|
1138
|
+
for(i = 0; i < number_of_criteria; i++) {
|
1139
|
+
current_value = arguments[i*2];
|
1140
|
+
if(current_value.type == ExcelRange) {
|
1141
|
+
criteria_range[i] = current_value;
|
1142
|
+
if(current_value.rows != sum_range_rows) return VALUE;
|
1143
|
+
if(current_value.columns != sum_range_columns) return VALUE;
|
1144
|
+
} else {
|
1145
|
+
if(sum_range_rows != 1) return VALUE;
|
1146
|
+
if(sum_range_columns != 1) return VALUE;
|
1147
|
+
ExcelValue *tmp_array2 = (ExcelValue*) new_excel_value_array(1);
|
1148
|
+
tmp_array2[0] = current_value;
|
1149
|
+
criteria_range[i] = new_excel_range(tmp_array2,1,1);
|
1150
|
+
}
|
1151
|
+
}
|
1152
|
+
|
1153
|
+
// Now go through and set up the criteria
|
1154
|
+
ExcelComparison *criteria = malloc(sizeof(ExcelComparison)*number_of_criteria);
|
1155
|
+
if(criteria == 0) {
|
1156
|
+
printf("Out of memory\n");
|
1157
|
+
exit(-1);
|
1158
|
+
}
|
1159
|
+
char *s;
|
1160
|
+
for(i = 0; i < number_of_criteria; i++) {
|
1161
|
+
current_value = arguments[(i*2)+1];
|
1162
|
+
|
1163
|
+
if(current_value.type == ExcelString) {
|
1164
|
+
s = current_value.string;
|
1165
|
+
if(s[0] == '<') {
|
1166
|
+
if( s[1] == '>') {
|
1167
|
+
criteria[i].type = NotEqual;
|
1168
|
+
criteria[i].comparator = new_excel_string(strndup(s+2,strlen(s)-2));
|
1169
|
+
} else if(s[1] == '=') {
|
1170
|
+
criteria[i].type = LessThanOrEqual;
|
1171
|
+
criteria[i].comparator = new_excel_string(strndup(s+2,strlen(s)-2));
|
1172
|
+
} else {
|
1173
|
+
criteria[i].type = LessThan;
|
1174
|
+
criteria[i].comparator = new_excel_string(strndup(s+1,strlen(s)-1));
|
1175
|
+
}
|
1176
|
+
} else if(s[0] == '>') {
|
1177
|
+
if(s[1] == '=') {
|
1178
|
+
criteria[i].type = MoreThanOrEqual;
|
1179
|
+
criteria[i].comparator = new_excel_string(strndup(s+2,strlen(s)-2));
|
1180
|
+
} else {
|
1181
|
+
criteria[i].type = MoreThan;
|
1182
|
+
criteria[i].comparator = new_excel_string(strndup(s+1,strlen(s)-1));
|
1183
|
+
}
|
1184
|
+
} else if(s[0] == '=') {
|
1185
|
+
criteria[i].type = Equal;
|
1186
|
+
criteria[i].comparator = new_excel_string(strndup(s+1,strlen(s)-1));
|
1187
|
+
} else {
|
1188
|
+
criteria[i].type = Equal;
|
1189
|
+
criteria[i].comparator = current_value;
|
1190
|
+
}
|
1191
|
+
} else {
|
1192
|
+
criteria[i].type = Equal;
|
1193
|
+
criteria[i].comparator = current_value;
|
1194
|
+
}
|
1195
|
+
}
|
1196
|
+
|
1197
|
+
double total = 0;
|
1198
|
+
int size = sum_range_columns * sum_range_rows;
|
1199
|
+
int j;
|
1200
|
+
int passed = 0;
|
1201
|
+
ExcelValue value_to_be_checked;
|
1202
|
+
ExcelComparison comparison;
|
1203
|
+
ExcelValue comparator;
|
1204
|
+
double number;
|
1205
|
+
// For each cell in the sum range
|
1206
|
+
for(j=0; j < size; j++ ) {
|
1207
|
+
passed = 1;
|
1208
|
+
for(i=0; i < number_of_criteria; i++) {
|
1209
|
+
value_to_be_checked = ((ExcelValue *) ((ExcelValue) criteria_range[i]).array)[j];
|
1210
|
+
comparison = criteria[i];
|
1211
|
+
comparator = comparison.comparator;
|
1212
|
+
|
1213
|
+
switch(value_to_be_checked.type) {
|
1214
|
+
case ExcelError: // Errors match only errors
|
1215
|
+
if(comparison.type != Equal) passed = 0;
|
1216
|
+
if(comparator.type != ExcelError) passed = 0;
|
1217
|
+
if(value_to_be_checked.number != comparator.number) passed = 0;
|
1218
|
+
break;
|
1219
|
+
case ExcelBoolean: // Booleans match only booleans (FIXME: I think?)
|
1220
|
+
if(comparison.type != Equal) passed = 0;
|
1221
|
+
if(comparator.type != ExcelBoolean ) passed = 0;
|
1222
|
+
if(value_to_be_checked.number != comparator.number) passed = 0;
|
1223
|
+
break;
|
1224
|
+
case ExcelEmpty:
|
1225
|
+
// if(comparator.type == ExcelEmpty) break; // FIXME: Huh? In excel blank doesn't match blank?!
|
1226
|
+
if(comparator.type != ExcelString) {
|
1227
|
+
passed = 0;
|
1228
|
+
break;
|
1229
|
+
} else {
|
1230
|
+
if(strlen(comparator.string) != 0) passed = 0; // Empty strings match blanks.
|
1231
|
+
break;
|
1232
|
+
}
|
1233
|
+
break;
|
1234
|
+
case ExcelNumber:
|
1235
|
+
if(comparator.type == ExcelNumber) {
|
1236
|
+
number = comparator.number;
|
1237
|
+
} else if(comparator.type == ExcelString) {
|
1238
|
+
number = number_from(comparator);
|
1239
|
+
if(conversion_error == 1) {
|
1240
|
+
conversion_error = 0;
|
1241
|
+
passed = 0;
|
1242
|
+
break;
|
1243
|
+
}
|
1244
|
+
} else {
|
1245
|
+
passed = 0;
|
1246
|
+
break;
|
1247
|
+
}
|
1248
|
+
switch(comparison.type) {
|
1249
|
+
case Equal:
|
1250
|
+
if(value_to_be_checked.number != number) passed = 0;
|
1251
|
+
break;
|
1252
|
+
case LessThan:
|
1253
|
+
if(value_to_be_checked.number >= number) passed = 0;
|
1254
|
+
break;
|
1255
|
+
case LessThanOrEqual:
|
1256
|
+
if(value_to_be_checked.number > number) passed = 0;
|
1257
|
+
break;
|
1258
|
+
case NotEqual:
|
1259
|
+
if(value_to_be_checked.number == number) passed = 0;
|
1260
|
+
break;
|
1261
|
+
case MoreThanOrEqual:
|
1262
|
+
if(value_to_be_checked.number < number) passed = 0;
|
1263
|
+
break;
|
1264
|
+
case MoreThan:
|
1265
|
+
if(value_to_be_checked.number <= number) passed = 0;
|
1266
|
+
break;
|
1267
|
+
}
|
1268
|
+
break;
|
1269
|
+
case ExcelString:
|
1270
|
+
// First case, the comparator is a number, simplification is that it can only be equal
|
1271
|
+
if(comparator.type == ExcelNumber) {
|
1272
|
+
if(comparison.type != Equal) {
|
1273
|
+
printf("This shouldn't be possible?");
|
1274
|
+
passed = 0;
|
1275
|
+
break;
|
1276
|
+
}
|
1277
|
+
number = number_from(value_to_be_checked);
|
1278
|
+
if(conversion_error == 1) {
|
1279
|
+
conversion_error = 0;
|
1280
|
+
passed = 0;
|
1281
|
+
break;
|
1282
|
+
}
|
1283
|
+
if(number != comparator.number) {
|
1284
|
+
passed = 0;
|
1285
|
+
break;
|
1286
|
+
} else {
|
1287
|
+
break;
|
1288
|
+
}
|
1289
|
+
// Second case, the comparator is also a string, so need to be able to do full range of tests
|
1290
|
+
} else if(comparator.type == ExcelString) {
|
1291
|
+
switch(comparison.type) {
|
1292
|
+
case Equal:
|
1293
|
+
if(excel_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
1294
|
+
break;
|
1295
|
+
case LessThan:
|
1296
|
+
if(less_than(value_to_be_checked,comparator).number == 0) passed = 0;
|
1297
|
+
break;
|
1298
|
+
case LessThanOrEqual:
|
1299
|
+
if(less_than_or_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
1300
|
+
break;
|
1301
|
+
case NotEqual:
|
1302
|
+
if(not_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
1303
|
+
break;
|
1304
|
+
case MoreThanOrEqual:
|
1305
|
+
if(more_than_or_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
1306
|
+
break;
|
1307
|
+
case MoreThan:
|
1308
|
+
if(more_than(value_to_be_checked,comparator).number == 0) passed = 0;
|
1309
|
+
break;
|
1310
|
+
}
|
1311
|
+
} else {
|
1312
|
+
passed = 0;
|
1313
|
+
break;
|
1314
|
+
}
|
1315
|
+
break;
|
1316
|
+
case ExcelRange:
|
1317
|
+
return VALUE;
|
1318
|
+
}
|
1319
|
+
if(passed == 0) break;
|
1320
|
+
}
|
1321
|
+
if(passed == 1) {
|
1322
|
+
current_value = sum_range[j];
|
1323
|
+
if(current_value.type == ExcelError) {
|
1324
|
+
return current_value;
|
1325
|
+
} else if(current_value.type == ExcelNumber) {
|
1326
|
+
total += current_value.number;
|
1327
|
+
}
|
1328
|
+
}
|
1329
|
+
}
|
1330
|
+
return new_excel_number(total);
|
1331
|
+
}
|
1332
|
+
|
1333
|
+
static ExcelValue sumif(ExcelValue check_range_v, ExcelValue criteria_v, ExcelValue sum_range_v ) {
|
1334
|
+
ExcelValue tmp_array_sumif[] = {check_range_v, criteria_v};
|
1335
|
+
return sumifs(sum_range_v,2,tmp_array_sumif);
|
1336
|
+
}
|
1337
|
+
|
1338
|
+
static ExcelValue sumif_2(ExcelValue check_range_v, ExcelValue criteria_v) {
|
1339
|
+
ExcelValue tmp_array_sumif2[] = {check_range_v, criteria_v};
|
1340
|
+
return sumifs(check_range_v,2,tmp_array_sumif2);
|
1341
|
+
}
|
1342
|
+
|
1343
|
+
static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments) {
|
1344
|
+
if(number_of_arguments <1) return VALUE;
|
1345
|
+
|
1346
|
+
int a;
|
1347
|
+
int i;
|
1348
|
+
int j;
|
1349
|
+
int rows;
|
1350
|
+
int columns;
|
1351
|
+
ExcelValue current_value;
|
1352
|
+
ExcelValue **ranges = malloc(sizeof(ExcelValue *)*number_of_arguments);
|
1353
|
+
if(ranges == 0) {
|
1354
|
+
printf("Out of memory\n");
|
1355
|
+
exit(-1);
|
1356
|
+
}
|
1357
|
+
double product = 1;
|
1358
|
+
double sum = 0;
|
1359
|
+
|
1360
|
+
// Find out dimensions of first argument
|
1361
|
+
if(arguments[0].type == ExcelRange) {
|
1362
|
+
rows = arguments[0].rows;
|
1363
|
+
columns = arguments[0].columns;
|
1364
|
+
} else {
|
1365
|
+
rows = 1;
|
1366
|
+
columns = 1;
|
1367
|
+
}
|
1368
|
+
// Extract arrays from each of the given ranges, checking for errors and bounds as we go
|
1369
|
+
for(a=0;a<number_of_arguments;a++) {
|
1370
|
+
current_value = arguments[a];
|
1371
|
+
switch(current_value.type) {
|
1372
|
+
case ExcelRange:
|
1373
|
+
if(current_value.rows != rows || current_value.columns != columns) return VALUE;
|
1374
|
+
ranges[a] = current_value.array;
|
1375
|
+
break;
|
1376
|
+
case ExcelError:
|
1377
|
+
return current_value;
|
1378
|
+
break;
|
1379
|
+
case ExcelEmpty:
|
1380
|
+
return VALUE;
|
1381
|
+
break;
|
1382
|
+
default:
|
1383
|
+
if(rows != 1 && columns !=1) return VALUE;
|
1384
|
+
ranges[a] = (ExcelValue*) new_excel_value_array(1);
|
1385
|
+
ranges[a][0] = arguments[a];
|
1386
|
+
break;
|
1387
|
+
}
|
1388
|
+
}
|
1389
|
+
|
1390
|
+
for(i=0;i<rows;i++) {
|
1391
|
+
for(j=0;j<columns;j++) {
|
1392
|
+
product = 1;
|
1393
|
+
for(a=0;a<number_of_arguments;a++) {
|
1394
|
+
current_value = ranges[a][(i*columns)+j];
|
1395
|
+
if(current_value.type == ExcelNumber) {
|
1396
|
+
product *= current_value.number;
|
1397
|
+
} else {
|
1398
|
+
product *= 0;
|
1399
|
+
}
|
1400
|
+
}
|
1401
|
+
sum += product;
|
1402
|
+
}
|
1403
|
+
}
|
1404
|
+
return new_excel_number(sum);
|
1405
|
+
}
|
1406
|
+
|
1407
|
+
static ExcelValue vlookup_3(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v) {
|
1408
|
+
return vlookup(lookup_value_v,lookup_table_v,column_number_v,TRUE);
|
1409
|
+
}
|
1410
|
+
|
1411
|
+
static ExcelValue vlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v, ExcelValue match_type_v) {
|
1412
|
+
CHECK_FOR_PASSED_ERROR(lookup_value_v)
|
1413
|
+
CHECK_FOR_PASSED_ERROR(lookup_table_v)
|
1414
|
+
CHECK_FOR_PASSED_ERROR(column_number_v)
|
1415
|
+
CHECK_FOR_PASSED_ERROR(match_type_v)
|
1416
|
+
|
1417
|
+
if(lookup_value_v.type == ExcelEmpty) return NA;
|
1418
|
+
if(lookup_table_v.type != ExcelRange) return NA;
|
1419
|
+
if(column_number_v.type != ExcelNumber) return NA;
|
1420
|
+
if(match_type_v.type != ExcelBoolean) return NA;
|
1421
|
+
|
1422
|
+
int i;
|
1423
|
+
int last_good_match = 0;
|
1424
|
+
int rows = lookup_table_v.rows;
|
1425
|
+
int columns = lookup_table_v.columns;
|
1426
|
+
ExcelValue *array = lookup_table_v.array;
|
1427
|
+
ExcelValue possible_match_v;
|
1428
|
+
|
1429
|
+
if(column_number_v.number > columns) return REF;
|
1430
|
+
if(column_number_v.number < 1) return VALUE;
|
1431
|
+
|
1432
|
+
if(match_type_v.number == false) { // Exact match required
|
1433
|
+
for(i=0; i< rows; i++) {
|
1434
|
+
possible_match_v = array[i*columns];
|
1435
|
+
if(excel_equal(lookup_value_v,possible_match_v).number == true) {
|
1436
|
+
return array[(i*columns)+(((int) column_number_v.number) - 1)];
|
1437
|
+
}
|
1438
|
+
}
|
1439
|
+
return NA;
|
1440
|
+
} else { // Highest value that is less than or equal
|
1441
|
+
for(i=0; i< rows; i++) {
|
1442
|
+
possible_match_v = array[i*columns];
|
1443
|
+
if(lookup_value_v.type != possible_match_v.type) continue;
|
1444
|
+
if(more_than(possible_match_v,lookup_value_v).number == true) {
|
1445
|
+
if(i == 0) return NA;
|
1446
|
+
return array[((i-1)*columns)+(((int) column_number_v.number) - 1)];
|
1447
|
+
} else {
|
1448
|
+
last_good_match = i;
|
1449
|
+
}
|
1450
|
+
}
|
1451
|
+
return array[(last_good_match*columns)+(((int) column_number_v.number) - 1)];
|
1452
|
+
}
|
1453
|
+
return NA;
|
1454
|
+
}
|
1455
|
+
|
1456
|
+
|
1457
|
+
|
1458
|
+
int test_functions() {
|
1459
|
+
// Test ABS
|
1460
|
+
assert(excel_abs(ONE).number == 1);
|
1461
|
+
assert(excel_abs(new_excel_number(-1)).number == 1);
|
1462
|
+
assert(excel_abs(VALUE).type == ExcelError);
|
1463
|
+
|
1464
|
+
// Test ADD
|
1465
|
+
assert(add(ONE,new_excel_number(-2.5)).number == -1.5);
|
1466
|
+
assert(add(ONE,VALUE).type == ExcelError);
|
1467
|
+
|
1468
|
+
// Test AND
|
1469
|
+
ExcelValue true_array1[] = { TRUE, new_excel_number(10)};
|
1470
|
+
ExcelValue true_array2[] = { ONE };
|
1471
|
+
ExcelValue false_array1[] = { FALSE, new_excel_number(10)};
|
1472
|
+
ExcelValue false_array2[] = { TRUE, new_excel_number(0)};
|
1473
|
+
// ExcelValue error_array1[] = { new_excel_number(10)}; // Not implemented
|
1474
|
+
ExcelValue error_array2[] = { TRUE, NA};
|
1475
|
+
assert(excel_and(2,true_array1).number == 1);
|
1476
|
+
assert(excel_and(1,true_array2).number == 1);
|
1477
|
+
assert(excel_and(2,false_array1).number == 0);
|
1478
|
+
assert(excel_and(2,false_array2).number == 0);
|
1479
|
+
// assert(excel_and(1,error_array1).type == ExcelError); // Not implemented
|
1480
|
+
assert(excel_and(2,error_array2).type == ExcelError);
|
1481
|
+
|
1482
|
+
// Test AVERAGE
|
1483
|
+
ExcelValue array1[] = { new_excel_number(10), new_excel_number(5), TRUE, FALSE};
|
1484
|
+
ExcelValue array1_v = new_excel_range(array1,2,2);
|
1485
|
+
ExcelValue array2[] = { array1_v, new_excel_number(9), new_excel_string("Hello")};
|
1486
|
+
ExcelValue array3[] = { array1_v, new_excel_number(9), new_excel_string("Hello"), VALUE};
|
1487
|
+
assert(average(4, array1).number == 7.5);
|
1488
|
+
assert(average(3, array2).number == 8);
|
1489
|
+
assert(average(4, array3).type == ExcelError);
|
1490
|
+
|
1491
|
+
// Test CHOOSE
|
1492
|
+
assert(choose(ONE,4,array1).number == 10);
|
1493
|
+
assert(choose(new_excel_number(4),4,array1).type == ExcelBoolean);
|
1494
|
+
assert(choose(new_excel_number(0),4,array1).type == ExcelError);
|
1495
|
+
assert(choose(new_excel_number(5),4,array1).type == ExcelError);
|
1496
|
+
assert(choose(ONE,4,array3).type == ExcelError);
|
1497
|
+
|
1498
|
+
// Test COUNT
|
1499
|
+
assert(count(4,array1).number == 2);
|
1500
|
+
assert(count(3,array2).number == 3);
|
1501
|
+
assert(count(4,array3).number == 3);
|
1502
|
+
|
1503
|
+
// Test COUNTA
|
1504
|
+
ExcelValue count_a_test_array_1[] = { new_excel_number(10), new_excel_number(5), TRUE, FALSE, new_excel_string("Hello"), VALUE, BLANK};
|
1505
|
+
ExcelValue count_a_test_array_1_v = new_excel_range(count_a_test_array_1,7,1);
|
1506
|
+
ExcelValue count_a_test_array_2[] = {new_excel_string("Bye"),count_a_test_array_1_v};
|
1507
|
+
assert(counta(7, count_a_test_array_1).number == 6);
|
1508
|
+
assert(counta(2, count_a_test_array_2).number == 7);
|
1509
|
+
|
1510
|
+
// Test divide
|
1511
|
+
assert(divide(new_excel_number(12.4),new_excel_number(3.2)).number == 3.875);
|
1512
|
+
assert(divide(new_excel_number(12.4),new_excel_number(0)).type == ExcelError);
|
1513
|
+
|
1514
|
+
// Test excel_equal
|
1515
|
+
assert(excel_equal(new_excel_number(1.2),new_excel_number(3.4)).type == ExcelBoolean);
|
1516
|
+
assert(excel_equal(new_excel_number(1.2),new_excel_number(3.4)).number == false);
|
1517
|
+
assert(excel_equal(new_excel_number(1.2),new_excel_number(1.2)).number == true);
|
1518
|
+
assert(excel_equal(new_excel_string("hello"), new_excel_string("HELLO")).number == true);
|
1519
|
+
assert(excel_equal(new_excel_string("hello world"), new_excel_string("HELLO")).number == false);
|
1520
|
+
assert(excel_equal(new_excel_string("1"), ONE).number == false);
|
1521
|
+
assert(excel_equal(DIV0, ONE).type == ExcelError);
|
1522
|
+
|
1523
|
+
// Test not_equal
|
1524
|
+
assert(not_equal(new_excel_number(1.2),new_excel_number(3.4)).type == ExcelBoolean);
|
1525
|
+
assert(not_equal(new_excel_number(1.2),new_excel_number(3.4)).number == true);
|
1526
|
+
assert(not_equal(new_excel_number(1.2),new_excel_number(1.2)).number == false);
|
1527
|
+
assert(not_equal(new_excel_string("hello"), new_excel_string("HELLO")).number == false);
|
1528
|
+
assert(not_equal(new_excel_string("hello world"), new_excel_string("HELLO")).number == true);
|
1529
|
+
assert(not_equal(new_excel_string("1"), ONE).number == true);
|
1530
|
+
assert(not_equal(DIV0, ONE).type == ExcelError);
|
1531
|
+
|
1532
|
+
// Test excel_if
|
1533
|
+
// Two argument version
|
1534
|
+
assert(excel_if_2(TRUE,new_excel_number(10)).type == ExcelNumber);
|
1535
|
+
assert(excel_if_2(TRUE,new_excel_number(10)).number == 10);
|
1536
|
+
assert(excel_if_2(FALSE,new_excel_number(10)).type == ExcelBoolean);
|
1537
|
+
assert(excel_if_2(FALSE,new_excel_number(10)).number == false);
|
1538
|
+
assert(excel_if_2(NA,new_excel_number(10)).type == ExcelError);
|
1539
|
+
// Three argument version
|
1540
|
+
assert(excel_if(TRUE,new_excel_number(10),new_excel_number(20)).type == ExcelNumber);
|
1541
|
+
assert(excel_if(TRUE,new_excel_number(10),new_excel_number(20)).number == 10);
|
1542
|
+
assert(excel_if(FALSE,new_excel_number(10),new_excel_number(20)).type == ExcelNumber);
|
1543
|
+
assert(excel_if(FALSE,new_excel_number(10),new_excel_number(20)).number == 20);
|
1544
|
+
assert(excel_if(NA,new_excel_number(10),new_excel_number(20)).type == ExcelError);
|
1545
|
+
|
1546
|
+
// Test excel_match
|
1547
|
+
ExcelValue excel_match_array_1[] = { new_excel_number(10), new_excel_number(100) };
|
1548
|
+
ExcelValue excel_match_array_1_v = new_excel_range(excel_match_array_1,1,2);
|
1549
|
+
ExcelValue excel_match_array_2[] = { new_excel_string("Pear"), new_excel_string("Bear"), new_excel_string("Apple") };
|
1550
|
+
ExcelValue excel_match_array_2_v = new_excel_range(excel_match_array_2,3,1);
|
1551
|
+
ExcelValue excel_match_array_4[] = { ONE, BLANK, new_excel_number(0) };
|
1552
|
+
ExcelValue excel_match_array_4_v = new_excel_range(excel_match_array_4,1,3);
|
1553
|
+
ExcelValue excel_match_array_5[] = { ONE, new_excel_number(0), BLANK };
|
1554
|
+
ExcelValue excel_match_array_5_v = new_excel_range(excel_match_array_5,1,3);
|
1555
|
+
|
1556
|
+
// Two argument version
|
1557
|
+
assert(excel_match_2(new_excel_number(10),excel_match_array_1_v).number == 1);
|
1558
|
+
assert(excel_match_2(new_excel_number(100),excel_match_array_1_v).number == 2);
|
1559
|
+
assert(excel_match_2(new_excel_number(1000),excel_match_array_1_v).type == ExcelError);
|
1560
|
+
assert(excel_match_2(new_excel_number(0), excel_match_array_4_v).number == 2);
|
1561
|
+
assert(excel_match_2(BLANK, excel_match_array_5_v).number == 2);
|
1562
|
+
|
1563
|
+
// Three argument version
|
1564
|
+
assert(excel_match(new_excel_number(10.0), excel_match_array_1_v, new_excel_number(0) ).number == 1);
|
1565
|
+
assert(excel_match(new_excel_number(100.0), excel_match_array_1_v, new_excel_number(0) ).number == 2);
|
1566
|
+
assert(excel_match(new_excel_number(1000.0), excel_match_array_1_v, new_excel_number(0) ).type == ExcelError);
|
1567
|
+
assert(excel_match(new_excel_string("bEAr"), excel_match_array_2_v, new_excel_number(0) ).number == 2);
|
1568
|
+
assert(excel_match(new_excel_number(1000.0), excel_match_array_1_v, ONE ).number == 2);
|
1569
|
+
assert(excel_match(new_excel_number(1.0), excel_match_array_1_v, ONE ).type == ExcelError);
|
1570
|
+
assert(excel_match(new_excel_string("Care"), excel_match_array_2_v, new_excel_number(-1) ).number == 1 );
|
1571
|
+
assert(excel_match(new_excel_string("Zebra"), excel_match_array_2_v, new_excel_number(-1) ).type == ExcelError);
|
1572
|
+
assert(excel_match(new_excel_string("a"), excel_match_array_2_v, new_excel_number(-1) ).number == 2);
|
1573
|
+
|
1574
|
+
// When not given a range
|
1575
|
+
assert(excel_match(new_excel_number(10.0), new_excel_number(10), new_excel_number(0.0)).number == 1);
|
1576
|
+
assert(excel_match(new_excel_number(20.0), new_excel_number(10), new_excel_number(0.0)).type == ExcelError);
|
1577
|
+
assert(excel_match(new_excel_number(10.0), excel_match_array_1_v, BLANK).number == 1);
|
1578
|
+
|
1579
|
+
// Test more than on
|
1580
|
+
// .. numbers
|
1581
|
+
assert(more_than(ONE,new_excel_number(2)).number == false);
|
1582
|
+
assert(more_than(ONE,ONE).number == false);
|
1583
|
+
assert(more_than(ONE,new_excel_number(0)).number == true);
|
1584
|
+
// .. booleans
|
1585
|
+
assert(more_than(FALSE,FALSE).number == false);
|
1586
|
+
assert(more_than(FALSE,TRUE).number == false);
|
1587
|
+
assert(more_than(TRUE,FALSE).number == true);
|
1588
|
+
assert(more_than(TRUE,TRUE).number == false);
|
1589
|
+
// ..strings
|
1590
|
+
assert(more_than(new_excel_string("HELLO"),new_excel_string("Ardvark")).number == true);
|
1591
|
+
assert(more_than(new_excel_string("HELLO"),new_excel_string("world")).number == false);
|
1592
|
+
assert(more_than(new_excel_string("HELLO"),new_excel_string("hello")).number == false);
|
1593
|
+
// ..blanks
|
1594
|
+
assert(more_than(BLANK,ONE).number == false);
|
1595
|
+
assert(more_than(BLANK,new_excel_number(-1)).number == true);
|
1596
|
+
assert(more_than(ONE,BLANK).number == true);
|
1597
|
+
assert(more_than(new_excel_number(-1),BLANK).number == false);
|
1598
|
+
|
1599
|
+
// Test less than on
|
1600
|
+
// .. numbers
|
1601
|
+
assert(less_than(ONE,new_excel_number(2)).number == true);
|
1602
|
+
assert(less_than(ONE,ONE).number == false);
|
1603
|
+
assert(less_than(ONE,new_excel_number(0)).number == false);
|
1604
|
+
// .. booleans
|
1605
|
+
assert(less_than(FALSE,FALSE).number == false);
|
1606
|
+
assert(less_than(FALSE,TRUE).number == true);
|
1607
|
+
assert(less_than(TRUE,FALSE).number == false);
|
1608
|
+
assert(less_than(TRUE,TRUE).number == false);
|
1609
|
+
// ..strings
|
1610
|
+
assert(less_than(new_excel_string("HELLO"),new_excel_string("Ardvark")).number == false);
|
1611
|
+
assert(less_than(new_excel_string("HELLO"),new_excel_string("world")).number == true);
|
1612
|
+
assert(less_than(new_excel_string("HELLO"),new_excel_string("hello")).number == false);
|
1613
|
+
// ..blanks
|
1614
|
+
assert(less_than(BLANK,ONE).number == true);
|
1615
|
+
assert(less_than(BLANK,new_excel_number(-1)).number == false);
|
1616
|
+
assert(less_than(ONE,BLANK).number == false);
|
1617
|
+
assert(less_than(new_excel_number(-1),BLANK).number == true);
|
1618
|
+
|
1619
|
+
// Test FIND function
|
1620
|
+
// ... should find the first occurrence of one string in another, returning :value if the string doesn't match
|
1621
|
+
assert(find_2(new_excel_string("one"),new_excel_string("onetwothree")).number == 1);
|
1622
|
+
assert(find_2(new_excel_string("one"),new_excel_string("twoonethree")).number == 4);
|
1623
|
+
assert(find_2(new_excel_string("one"),new_excel_string("twoonthree")).type == ExcelError);
|
1624
|
+
// ... should find the first occurrence of one string in another after a given index, returning :value if the string doesn't match
|
1625
|
+
assert(find(new_excel_string("one"),new_excel_string("onetwothree"),ONE).number == 1);
|
1626
|
+
assert(find(new_excel_string("one"),new_excel_string("twoonethree"),new_excel_number(5)).type == ExcelError);
|
1627
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_number(2)).number == 4);
|
1628
|
+
// ... should be possible for the start_num to be a string, if that string converts to a number
|
1629
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_string("2")).number == 4);
|
1630
|
+
// ... should return a :value error when given anything but a number as the third argument
|
1631
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_string("a")).type == ExcelError);
|
1632
|
+
// ... should return a :value error when given a third argument that is less than 1 or greater than the length of the string
|
1633
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_number(0)).type == ExcelError);
|
1634
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_number(-1)).type == ExcelError);
|
1635
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_number(7)).type == ExcelError);
|
1636
|
+
// ... BLANK in the first argument matches any character
|
1637
|
+
assert(find_2(BLANK,new_excel_string("abcdefg")).number == 1);
|
1638
|
+
assert(find(BLANK,new_excel_string("abcdefg"),new_excel_number(4)).number == 4);
|
1639
|
+
// ... should treat BLANK in the second argument as an empty string
|
1640
|
+
assert(find_2(BLANK,BLANK).number == 1);
|
1641
|
+
assert(find_2(new_excel_string("a"),BLANK).type == ExcelError);
|
1642
|
+
// ... should return an error if any argument is an error
|
1643
|
+
assert(find(new_excel_string("one"),new_excel_string("onetwothree"),NA).type == ExcelError);
|
1644
|
+
assert(find(new_excel_string("one"),NA,ONE).type == ExcelError);
|
1645
|
+
assert(find(NA,new_excel_string("onetwothree"),ONE).type == ExcelError);
|
1646
|
+
|
1647
|
+
// Test the IFERROR function
|
1648
|
+
assert(iferror(new_excel_string("ok"),ONE).type == ExcelString);
|
1649
|
+
assert(iferror(VALUE,ONE).type == ExcelNumber);
|
1650
|
+
|
1651
|
+
// Test the INDEX function
|
1652
|
+
ExcelValue index_array_1[] = { new_excel_number(10), new_excel_number(20), BLANK };
|
1653
|
+
ExcelValue index_array_1_v_column = new_excel_range(index_array_1,3,1);
|
1654
|
+
ExcelValue index_array_1_v_row = new_excel_range(index_array_1,1,3);
|
1655
|
+
ExcelValue index_array_2[] = { BLANK, ONE, new_excel_number(10), new_excel_number(11), new_excel_number(100), new_excel_number(101) };
|
1656
|
+
ExcelValue index_array_2_v = new_excel_range(index_array_2,3,2);
|
1657
|
+
// ... if given one argument should return the value at that offset in the range
|
1658
|
+
assert(excel_index_2(index_array_1_v_column,new_excel_number(2.0)).number == 20);
|
1659
|
+
assert(excel_index_2(index_array_1_v_row,new_excel_number(2.0)).number == 20);
|
1660
|
+
// ... but not if the range is not a single row or single column
|
1661
|
+
assert(excel_index_2(index_array_2_v,new_excel_number(2.0)).type == ExcelError);
|
1662
|
+
// ... it should return the value in the array at position row_number, column_number
|
1663
|
+
assert(excel_index(new_excel_number(10),ONE,ONE).number == 10);
|
1664
|
+
assert(excel_index(index_array_2_v,new_excel_number(1.0),new_excel_number(2.0)).number == 1);
|
1665
|
+
assert(excel_index(index_array_2_v,new_excel_number(2.0),new_excel_number(1.0)).number == 10);
|
1666
|
+
assert(excel_index(index_array_2_v,new_excel_number(3.0),new_excel_number(1.0)).number == 100);
|
1667
|
+
assert(excel_index(index_array_2_v,new_excel_number(3.0),new_excel_number(3.0)).type == ExcelError);
|
1668
|
+
// ... it should return ZERO not blank, if a blank cell is picked
|
1669
|
+
assert(excel_index(index_array_2_v,new_excel_number(1.0),new_excel_number(1.0)).type == ExcelNumber);
|
1670
|
+
assert(excel_index(index_array_2_v,new_excel_number(1.0),new_excel_number(1.0)).number == 0);
|
1671
|
+
assert(excel_index_2(index_array_1_v_row,new_excel_number(3.0)).type == ExcelNumber);
|
1672
|
+
assert(excel_index_2(index_array_1_v_row,new_excel_number(3.0)).number == 0);
|
1673
|
+
// ... it should return the whole row if given a zero column number
|
1674
|
+
ExcelValue index_result_1_v = excel_index(index_array_2_v,new_excel_number(1.0),new_excel_number(0.0));
|
1675
|
+
assert(index_result_1_v.type == ExcelRange);
|
1676
|
+
assert(index_result_1_v.rows == 1);
|
1677
|
+
assert(index_result_1_v.columns == 2);
|
1678
|
+
ExcelValue *index_result_1_a = index_result_1_v.array;
|
1679
|
+
assert(index_result_1_a[0].number == 0);
|
1680
|
+
assert(index_result_1_a[1].number == 1);
|
1681
|
+
// ... it should return the whole column if given a zero row number
|
1682
|
+
ExcelValue index_result_2_v = excel_index(index_array_2_v,new_excel_number(0),new_excel_number(1.0));
|
1683
|
+
assert(index_result_2_v.type == ExcelRange);
|
1684
|
+
assert(index_result_2_v.rows == 3);
|
1685
|
+
assert(index_result_2_v.columns == 1);
|
1686
|
+
ExcelValue *index_result_2_a = index_result_2_v.array;
|
1687
|
+
assert(index_result_2_a[0].number == 0);
|
1688
|
+
assert(index_result_2_a[1].number == 10);
|
1689
|
+
assert(index_result_2_a[2].number == 100);
|
1690
|
+
// ... it should return a :ref error when given arguments outside array range
|
1691
|
+
assert(excel_index_2(index_array_1_v_row,new_excel_number(-1)).type == ExcelError);
|
1692
|
+
assert(excel_index_2(index_array_1_v_row,new_excel_number(4)).type == ExcelError);
|
1693
|
+
// ... it should treat BLANK as zero if given as a required row or column number
|
1694
|
+
assert(excel_index(index_array_2_v,new_excel_number(1.0),BLANK).type == ExcelRange);
|
1695
|
+
assert(excel_index(index_array_2_v,BLANK,new_excel_number(2.0)).type == ExcelRange);
|
1696
|
+
// ... it should return an error if an argument is an error
|
1697
|
+
assert(excel_index(NA,NA,NA).type == ExcelError);
|
1698
|
+
|
1699
|
+
// LEFT(string,[characters])
|
1700
|
+
// ... should return the left n characters from a string
|
1701
|
+
assert(strcmp(left_1(new_excel_string("ONE")).string,"O") == 0);
|
1702
|
+
assert(strcmp(left(new_excel_string("ONE"),ONE).string,"O") == 0);
|
1703
|
+
assert(strcmp(left(new_excel_string("ONE"),new_excel_number(3)).string,"ONE") == 0);
|
1704
|
+
// ... should turn numbers into strings before processing
|
1705
|
+
assert(strcmp(left(new_excel_number(1.31e12),new_excel_number(3)).string, "131") == 0);
|
1706
|
+
// ... should turn booleans into the words TRUE and FALSE before processing
|
1707
|
+
assert(strcmp(left(TRUE,new_excel_number(3)).string,"TRU") == 0);
|
1708
|
+
assert(strcmp(left(FALSE,new_excel_number(3)).string,"FAL") == 0);
|
1709
|
+
// ... should return BLANK if given BLANK for either argument
|
1710
|
+
assert(left(BLANK,new_excel_number(3)).type == ExcelEmpty);
|
1711
|
+
assert(left(new_excel_string("ONE"),BLANK).type == ExcelEmpty);
|
1712
|
+
// ... should return an error if an argument is an error
|
1713
|
+
assert(left_1(NA).type == ExcelError);
|
1714
|
+
assert(left(new_excel_string("ONE"),NA).type == ExcelError);
|
1715
|
+
|
1716
|
+
// Test less than or equal to
|
1717
|
+
// .. numbers
|
1718
|
+
assert(less_than_or_equal(ONE,new_excel_number(2)).number == true);
|
1719
|
+
assert(less_than_or_equal(ONE,ONE).number == true);
|
1720
|
+
assert(less_than_or_equal(ONE,new_excel_number(0)).number == false);
|
1721
|
+
// .. booleans
|
1722
|
+
assert(less_than_or_equal(FALSE,FALSE).number == true);
|
1723
|
+
assert(less_than_or_equal(FALSE,TRUE).number == true);
|
1724
|
+
assert(less_than_or_equal(TRUE,FALSE).number == false);
|
1725
|
+
assert(less_than_or_equal(TRUE,TRUE).number == true);
|
1726
|
+
// ..strings
|
1727
|
+
assert(less_than_or_equal(new_excel_string("HELLO"),new_excel_string("Ardvark")).number == false);
|
1728
|
+
assert(less_than_or_equal(new_excel_string("HELLO"),new_excel_string("world")).number == true);
|
1729
|
+
assert(less_than_or_equal(new_excel_string("HELLO"),new_excel_string("hello")).number == true);
|
1730
|
+
// ..blanks
|
1731
|
+
assert(less_than_or_equal(BLANK,ONE).number == true);
|
1732
|
+
assert(less_than_or_equal(BLANK,new_excel_number(-1)).number == false);
|
1733
|
+
assert(less_than_or_equal(ONE,BLANK).number == false);
|
1734
|
+
assert(less_than_or_equal(new_excel_number(-1),BLANK).number == true);
|
1735
|
+
|
1736
|
+
// Test MAX
|
1737
|
+
assert(max(4, array1).number == 10);
|
1738
|
+
assert(max(3, array2).number == 10);
|
1739
|
+
assert(max(4, array3).type == ExcelError);
|
1740
|
+
|
1741
|
+
// Test MIN
|
1742
|
+
assert(min(4, array1).number == 5);
|
1743
|
+
assert(min(3, array2).number == 5);
|
1744
|
+
assert(min(4, array3).type == ExcelError);
|
1745
|
+
|
1746
|
+
// Test MOD
|
1747
|
+
// ... should return the remainder of a number
|
1748
|
+
assert(mod(new_excel_number(10), new_excel_number(3)).number == 1.0);
|
1749
|
+
assert(mod(new_excel_number(10), new_excel_number(5)).number == 0.0);
|
1750
|
+
// ... should be possible for the the arguments to be strings, if they convert to a number
|
1751
|
+
assert(mod(new_excel_string("3.5"),new_excel_string("2")).number == 1.5);
|
1752
|
+
// ... should treat BLANK as zero
|
1753
|
+
assert(mod(BLANK,new_excel_number(10)).number == 0);
|
1754
|
+
assert(mod(new_excel_number(10),BLANK).type == ExcelError);
|
1755
|
+
assert(mod(BLANK,BLANK).type == ExcelError);
|
1756
|
+
// ... should treat true as 1 and FALSE as 0
|
1757
|
+
assert((mod(new_excel_number(1.1),TRUE).number - 0.1) < 0.001);
|
1758
|
+
assert(mod(new_excel_number(1.1),FALSE).type == ExcelError);
|
1759
|
+
assert(mod(FALSE,new_excel_number(10)).number == 0);
|
1760
|
+
// ... should return an error when given inappropriate arguments
|
1761
|
+
assert(mod(new_excel_string("Asdasddf"),new_excel_string("adsfads")).type == ExcelError);
|
1762
|
+
// ... should return an error if an argument is an error
|
1763
|
+
assert(mod(new_excel_number(1),VALUE).type == ExcelError);
|
1764
|
+
assert(mod(VALUE,new_excel_number(1)).type == ExcelError);
|
1765
|
+
assert(mod(VALUE,VALUE).type == ExcelError);
|
1766
|
+
|
1767
|
+
// Test more than or equal to on
|
1768
|
+
// .. numbers
|
1769
|
+
assert(more_than_or_equal(ONE,new_excel_number(2)).number == false);
|
1770
|
+
assert(more_than_or_equal(ONE,ONE).number == true);
|
1771
|
+
assert(more_than_or_equal(ONE,new_excel_number(0)).number == true);
|
1772
|
+
// .. booleans
|
1773
|
+
assert(more_than_or_equal(FALSE,FALSE).number == true);
|
1774
|
+
assert(more_than_or_equal(FALSE,TRUE).number == false);
|
1775
|
+
assert(more_than_or_equal(TRUE,FALSE).number == true);
|
1776
|
+
assert(more_than_or_equal(TRUE,TRUE).number == true);
|
1777
|
+
// ..strings
|
1778
|
+
assert(more_than_or_equal(new_excel_string("HELLO"),new_excel_string("Ardvark")).number == true);
|
1779
|
+
assert(more_than_or_equal(new_excel_string("HELLO"),new_excel_string("world")).number == false);
|
1780
|
+
assert(more_than_or_equal(new_excel_string("HELLO"),new_excel_string("hello")).number == true);
|
1781
|
+
// ..blanks
|
1782
|
+
assert(more_than_or_equal(BLANK,BLANK).number == true);
|
1783
|
+
assert(more_than_or_equal(BLANK,ONE).number == false);
|
1784
|
+
assert(more_than_or_equal(BLANK,new_excel_number(-1)).number == true);
|
1785
|
+
assert(more_than_or_equal(ONE,BLANK).number == true);
|
1786
|
+
assert(more_than_or_equal(new_excel_number(-1),BLANK).number == false);
|
1787
|
+
|
1788
|
+
// Test negative
|
1789
|
+
// ... should return the negative of its arguments
|
1790
|
+
assert(negative(new_excel_number(1)).number == -1);
|
1791
|
+
assert(negative(new_excel_number(-1)).number == 1);
|
1792
|
+
// ... should treat strings that only contain numbers as numbers
|
1793
|
+
assert(negative(new_excel_string("10")).number == -10);
|
1794
|
+
assert(negative(new_excel_string("-1.3")).number == 1.3);
|
1795
|
+
// ... should return an error when given inappropriate arguments
|
1796
|
+
assert(negative(new_excel_string("Asdasddf")).type == ExcelError);
|
1797
|
+
// ... should treat BLANK as zero
|
1798
|
+
assert(negative(BLANK).number == 0);
|
1799
|
+
|
1800
|
+
// Test PMT(rate,number_of_periods,present_value) - optional arguments not yet implemented
|
1801
|
+
// ... should calculate the monthly payment required for a given principal, interest rate and loan period
|
1802
|
+
assert((pmt(new_excel_number(0.1),new_excel_number(10),new_excel_number(100)).number - -16.27) < 0.01);
|
1803
|
+
assert((pmt(new_excel_number(0.0123),new_excel_number(99.1),new_excel_number(123.32)).number - -2.159) < 0.01);
|
1804
|
+
assert((pmt(new_excel_number(0),new_excel_number(2),new_excel_number(10)).number - -5) < 0.01);
|
1805
|
+
|
1806
|
+
// Test power
|
1807
|
+
// ... should return sum of its arguments
|
1808
|
+
assert(power(new_excel_number(2),new_excel_number(3)).number == 8);
|
1809
|
+
assert(power(new_excel_number(4.0),new_excel_number(0.5)).number == 2.0);
|
1810
|
+
|
1811
|
+
// Test round
|
1812
|
+
assert(excel_round(new_excel_number(1.1), new_excel_number(0)).number == 1.0);
|
1813
|
+
assert(excel_round(new_excel_number(1.5), new_excel_number(0)).number == 2.0);
|
1814
|
+
assert(excel_round(new_excel_number(1.56),new_excel_number(1)).number == 1.6);
|
1815
|
+
assert(excel_round(new_excel_number(-1.56),new_excel_number(1)).number == -1.6);
|
1816
|
+
|
1817
|
+
// Test rounddown
|
1818
|
+
assert(rounddown(new_excel_number(1.1), new_excel_number(0)).number == 1.0);
|
1819
|
+
assert(rounddown(new_excel_number(1.5), new_excel_number(0)).number == 1.0);
|
1820
|
+
assert(rounddown(new_excel_number(1.56),new_excel_number(1)).number == 1.5);
|
1821
|
+
assert(rounddown(new_excel_number(-1.56),new_excel_number(1)).number == -1.5);
|
1822
|
+
|
1823
|
+
// Test roundup
|
1824
|
+
assert(roundup(new_excel_number(1.1), new_excel_number(0)).number == 2.0);
|
1825
|
+
assert(roundup(new_excel_number(1.5), new_excel_number(0)).number == 2.0);
|
1826
|
+
assert(roundup(new_excel_number(1.56),new_excel_number(1)).number == 1.6);
|
1827
|
+
assert(roundup(new_excel_number(-1.56),new_excel_number(1)).number == -1.6);
|
1828
|
+
|
1829
|
+
// Test string joining
|
1830
|
+
ExcelValue string_join_array_1[] = {new_excel_string("Hello "), new_excel_string("world")};
|
1831
|
+
ExcelValue string_join_array_2[] = {new_excel_string("Hello "), new_excel_string("world"), new_excel_string("!")};
|
1832
|
+
ExcelValue string_join_array_3[] = {new_excel_string("Top "), new_excel_number(10.0)};
|
1833
|
+
ExcelValue string_join_array_4[] = {new_excel_string("Top "), new_excel_number(10.5)};
|
1834
|
+
ExcelValue string_join_array_5[] = {new_excel_string("Top "), TRUE, FALSE};
|
1835
|
+
// ... should return a string by combining its arguments
|
1836
|
+
// inspect_excel_value(string_join(2, string_join_array_1));
|
1837
|
+
assert(string_join(2, string_join_array_1).string[6] == 'w');
|
1838
|
+
// ... should cope with an arbitrary number of arguments
|
1839
|
+
assert(string_join(3, string_join_array_2).string[11] == '!');
|
1840
|
+
// ... should convert values to strings as it goes
|
1841
|
+
assert(string_join(2, string_join_array_3).string[4] == '1');
|
1842
|
+
// ... should convert integer values into strings without decimal points
|
1843
|
+
assert(string_join(2, string_join_array_3).string[7] == '\0');
|
1844
|
+
assert(string_join(2, string_join_array_4).string[7] == '5');
|
1845
|
+
// ... should convert TRUE and FALSE into strings
|
1846
|
+
assert(string_join(3,string_join_array_5).string[4] == 'T');
|
1847
|
+
|
1848
|
+
// Test SUBTOTAL function
|
1849
|
+
ExcelValue subtotal_array_1[] = {new_excel_number(10),new_excel_number(100),BLANK};
|
1850
|
+
ExcelValue subtotal_array_1_v = new_excel_range(subtotal_array_1,3,1);
|
1851
|
+
ExcelValue subtotal_array_2[] = {new_excel_number(1),new_excel_string("two"),subtotal_array_1_v};
|
1852
|
+
|
1853
|
+
assert(subtotal(new_excel_number(1.0),3,subtotal_array_2).number == 111.0/3.0);
|
1854
|
+
assert(subtotal(new_excel_number(2.0),3,subtotal_array_2).number == 3);
|
1855
|
+
assert(subtotal(new_excel_number(3.0),7, count_a_test_array_1).number == 6);
|
1856
|
+
assert(subtotal(new_excel_number(3.0),3,subtotal_array_2).number == 4);
|
1857
|
+
assert(subtotal(new_excel_number(9.0),3,subtotal_array_2).number == 111);
|
1858
|
+
assert(subtotal(new_excel_number(101.0),3,subtotal_array_2).number == 111.0/3.0);
|
1859
|
+
assert(subtotal(new_excel_number(102.0),3,subtotal_array_2).number == 3);
|
1860
|
+
assert(subtotal(new_excel_number(103.0),3,subtotal_array_2).number == 4);
|
1861
|
+
assert(subtotal(new_excel_number(109.0),3,subtotal_array_2).number == 111);
|
1862
|
+
|
1863
|
+
// Test SUMIFS function
|
1864
|
+
ExcelValue sumifs_array_1[] = {new_excel_number(10),new_excel_number(100),BLANK};
|
1865
|
+
ExcelValue sumifs_array_1_v = new_excel_range(sumifs_array_1,3,1);
|
1866
|
+
ExcelValue sumifs_array_2[] = {new_excel_string("pear"),new_excel_string("bear"),new_excel_string("apple")};
|
1867
|
+
ExcelValue sumifs_array_2_v = new_excel_range(sumifs_array_2,3,1);
|
1868
|
+
ExcelValue sumifs_array_3[] = {new_excel_number(1),new_excel_number(2),new_excel_number(3),new_excel_number(4),new_excel_number(5),new_excel_number(5)};
|
1869
|
+
ExcelValue sumifs_array_3_v = new_excel_range(sumifs_array_3,6,1);
|
1870
|
+
ExcelValue sumifs_array_4[] = {new_excel_string("CO2"),new_excel_string("CH4"),new_excel_string("N2O"),new_excel_string("CH4"),new_excel_string("N2O"),new_excel_string("CO2")};
|
1871
|
+
ExcelValue sumifs_array_4_v = new_excel_range(sumifs_array_4,6,1);
|
1872
|
+
ExcelValue sumifs_array_5[] = {new_excel_string("1A"),new_excel_string("1A"),new_excel_string("1A"),new_excel_number(4),new_excel_number(4),new_excel_number(5)};
|
1873
|
+
ExcelValue sumifs_array_5_v = new_excel_range(sumifs_array_5,6,1);
|
1874
|
+
|
1875
|
+
// ... should only sum values that meet all of the criteria
|
1876
|
+
ExcelValue sumifs_array_6[] = { sumifs_array_1_v, new_excel_number(10), sumifs_array_2_v, new_excel_string("Bear") };
|
1877
|
+
assert(sumifs(sumifs_array_1_v,4,sumifs_array_6).number == 0.0);
|
1878
|
+
|
1879
|
+
ExcelValue sumifs_array_7[] = { sumifs_array_1_v, new_excel_number(10), sumifs_array_2_v, new_excel_string("Pear") };
|
1880
|
+
assert(sumifs(sumifs_array_1_v,4,sumifs_array_7).number == 10.0);
|
1881
|
+
|
1882
|
+
// ... should work when single cells are given where ranges expected
|
1883
|
+
ExcelValue sumifs_array_8[] = { new_excel_string("CAR"), new_excel_string("CAR"), new_excel_string("FCV"), new_excel_string("FCV")};
|
1884
|
+
assert(sumifs(new_excel_number(0.143897265452564), 4, sumifs_array_8).number == 0.143897265452564);
|
1885
|
+
|
1886
|
+
// ... should match numbers with strings that contain numbers
|
1887
|
+
ExcelValue sumifs_array_9[] = { new_excel_number(10), new_excel_string("10.0")};
|
1888
|
+
assert(sumifs(new_excel_number(100),2,sumifs_array_9).number == 100);
|
1889
|
+
|
1890
|
+
ExcelValue sumifs_array_10[] = { sumifs_array_4_v, new_excel_string("CO2"), sumifs_array_5_v, new_excel_number(2)};
|
1891
|
+
assert(sumifs(sumifs_array_3_v,4, sumifs_array_10).number == 0);
|
1892
|
+
|
1893
|
+
// ... should match with strings that contain criteria
|
1894
|
+
ExcelValue sumifs_array_10a[] = { sumifs_array_3_v, new_excel_string("=5")};
|
1895
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10a).number == 10);
|
1896
|
+
|
1897
|
+
ExcelValue sumifs_array_10b[] = { sumifs_array_3_v, new_excel_string("<>3")};
|
1898
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10b).number == 17);
|
1899
|
+
|
1900
|
+
ExcelValue sumifs_array_10c[] = { sumifs_array_3_v, new_excel_string("<3")};
|
1901
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10c).number == 3);
|
1902
|
+
|
1903
|
+
ExcelValue sumifs_array_10d[] = { sumifs_array_3_v, new_excel_string("<=3")};
|
1904
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10d).number == 6);
|
1905
|
+
|
1906
|
+
ExcelValue sumifs_array_10e[] = { sumifs_array_3_v, new_excel_string(">3")};
|
1907
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10e).number == 14);
|
1908
|
+
|
1909
|
+
ExcelValue sumifs_array_10f[] = { sumifs_array_3_v, new_excel_string(">=3")};
|
1910
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10f).number == 17);
|
1911
|
+
|
1912
|
+
// ... should treat BLANK as an empty string when in the check_range, but not in the criteria
|
1913
|
+
ExcelValue sumifs_array_11[] = { BLANK, new_excel_number(20)};
|
1914
|
+
assert(sumifs(new_excel_number(100),2,sumifs_array_11).number == 0);
|
1915
|
+
|
1916
|
+
ExcelValue sumifs_array_12[] = {BLANK, new_excel_string("")};
|
1917
|
+
assert(sumifs(new_excel_number(100),2,sumifs_array_12).number == 100);
|
1918
|
+
|
1919
|
+
ExcelValue sumifs_array_13[] = {BLANK, BLANK};
|
1920
|
+
assert(sumifs(new_excel_number(100),2,sumifs_array_13).number == 0);
|
1921
|
+
|
1922
|
+
// ... should return an error if range argument is an error
|
1923
|
+
assert(sumifs(REF,2,sumifs_array_13).type == ExcelError);
|
1924
|
+
|
1925
|
+
|
1926
|
+
// Test SUMIF
|
1927
|
+
// ... where there is only a check range
|
1928
|
+
assert(sumif_2(sumifs_array_1_v,new_excel_string(">0")).number == 110.0);
|
1929
|
+
assert(sumif_2(sumifs_array_1_v,new_excel_string(">10")).number == 100.0);
|
1930
|
+
assert(sumif_2(sumifs_array_1_v,new_excel_string("<100")).number == 10.0);
|
1931
|
+
|
1932
|
+
// ... where there is a seprate sum range
|
1933
|
+
ExcelValue sumif_array_1[] = {new_excel_number(15),new_excel_number(20), new_excel_number(30)};
|
1934
|
+
ExcelValue sumif_array_1_v = new_excel_range(sumif_array_1,3,1);
|
1935
|
+
assert(sumif(sumifs_array_1_v,new_excel_string("10"),sumif_array_1_v).number == 15);
|
1936
|
+
|
1937
|
+
|
1938
|
+
// Test SUMPRODUCT
|
1939
|
+
ExcelValue sumproduct_1[] = { new_excel_number(10), new_excel_number(100), BLANK};
|
1940
|
+
ExcelValue sumproduct_2[] = { BLANK, new_excel_number(100), new_excel_number(10), BLANK};
|
1941
|
+
ExcelValue sumproduct_3[] = { BLANK };
|
1942
|
+
ExcelValue sumproduct_4[] = { new_excel_number(10), new_excel_number(100), new_excel_number(1000)};
|
1943
|
+
ExcelValue sumproduct_5[] = { new_excel_number(1), new_excel_number(2), new_excel_number(3)};
|
1944
|
+
ExcelValue sumproduct_6[] = { new_excel_number(1), new_excel_number(2), new_excel_number(4), new_excel_number(5)};
|
1945
|
+
ExcelValue sumproduct_7[] = { new_excel_number(10), new_excel_number(20), new_excel_number(40), new_excel_number(50)};
|
1946
|
+
ExcelValue sumproduct_8[] = { new_excel_number(11), new_excel_number(21), new_excel_number(41), new_excel_number(51)};
|
1947
|
+
ExcelValue sumproduct_9[] = { BLANK, BLANK };
|
1948
|
+
|
1949
|
+
ExcelValue sumproduct_1_v = new_excel_range( sumproduct_1, 3, 1);
|
1950
|
+
ExcelValue sumproduct_2_v = new_excel_range( sumproduct_2, 3, 1);
|
1951
|
+
ExcelValue sumproduct_3_v = new_excel_range( sumproduct_3, 1, 1);
|
1952
|
+
// ExcelValue sumproduct_4_v = new_excel_range( sumproduct_4, 1, 3); // Unused
|
1953
|
+
ExcelValue sumproduct_5_v = new_excel_range( sumproduct_5, 3, 1);
|
1954
|
+
ExcelValue sumproduct_6_v = new_excel_range( sumproduct_6, 2, 2);
|
1955
|
+
ExcelValue sumproduct_7_v = new_excel_range( sumproduct_7, 2, 2);
|
1956
|
+
ExcelValue sumproduct_8_v = new_excel_range( sumproduct_8, 2, 2);
|
1957
|
+
ExcelValue sumproduct_9_v = new_excel_range( sumproduct_9, 2, 1);
|
1958
|
+
|
1959
|
+
// ... should multiply together and then sum the elements in row or column areas given as arguments
|
1960
|
+
ExcelValue sumproducta_1[] = {sumproduct_1_v, sumproduct_2_v};
|
1961
|
+
assert(sumproduct(2,sumproducta_1).number == 100*100);
|
1962
|
+
|
1963
|
+
// ... should return :value when miss-matched array sizes
|
1964
|
+
ExcelValue sumproducta_2[] = {sumproduct_1_v, sumproduct_3_v};
|
1965
|
+
assert(sumproduct(2,sumproducta_2).type == ExcelError);
|
1966
|
+
|
1967
|
+
// ... if all its arguments are single values, should multiply them together
|
1968
|
+
// ExcelValue *sumproducta_3 = sumproduct_4;
|
1969
|
+
assert(sumproduct(3,sumproduct_4).number == 10*100*1000);
|
1970
|
+
|
1971
|
+
// ... if it only has one range as an argument, should add its elements together
|
1972
|
+
ExcelValue sumproducta_4[] = {sumproduct_5_v};
|
1973
|
+
assert(sumproduct(1,sumproducta_4).number == 1 + 2 + 3);
|
1974
|
+
|
1975
|
+
// ... if given multi row and column areas as arguments, should multipy the corresponding cell in each area and then add them all
|
1976
|
+
ExcelValue sumproducta_5[] = {sumproduct_6_v, sumproduct_7_v, sumproduct_8_v};
|
1977
|
+
assert(sumproduct(3,sumproducta_5).number == 1*10*11 + 2*20*21 + 4*40*41 + 5*50*51);
|
1978
|
+
|
1979
|
+
// ... should raise an error if BLANK values outside of an array
|
1980
|
+
ExcelValue sumproducta_6[] = {BLANK,new_excel_number(1)};
|
1981
|
+
assert(sumproduct(2,sumproducta_6).type == ExcelError);
|
1982
|
+
|
1983
|
+
// ... should ignore non-numeric values within an array
|
1984
|
+
ExcelValue sumproducta_7[] = {sumproduct_9_v, sumproduct_9_v};
|
1985
|
+
assert(sumproduct(2,sumproducta_7).number == 0);
|
1986
|
+
|
1987
|
+
// ... should return an error if an argument is an error
|
1988
|
+
ExcelValue sumproducta_8[] = {VALUE};
|
1989
|
+
assert(sumproduct(1,sumproducta_8).type == ExcelError);
|
1990
|
+
|
1991
|
+
// Test VLOOKUP
|
1992
|
+
ExcelValue vlookup_a1[] = {new_excel_number(1),new_excel_number(10),new_excel_number(2),new_excel_number(20),new_excel_number(3),new_excel_number(30)};
|
1993
|
+
ExcelValue vlookup_a2[] = {new_excel_string("hello"),new_excel_number(10),new_excel_number(2),new_excel_number(20),new_excel_number(3),new_excel_number(30)};
|
1994
|
+
ExcelValue vlookup_a3[] = {BLANK,new_excel_number(10),new_excel_number(2),new_excel_number(20),new_excel_number(3),new_excel_number(30)};
|
1995
|
+
ExcelValue vlookup_a1_v = new_excel_range(vlookup_a1,3,2);
|
1996
|
+
ExcelValue vlookup_a2_v = new_excel_range(vlookup_a2,3,2);
|
1997
|
+
ExcelValue vlookup_a3_v = new_excel_range(vlookup_a3,3,2);
|
1998
|
+
// ... should match the first argument against the first column of the table in the second argument, returning the value in the column specified by the third argument
|
1999
|
+
assert(vlookup_3(new_excel_number(2.0),vlookup_a1_v,new_excel_number(2)).number == 20);
|
2000
|
+
assert(vlookup_3(new_excel_number(1.5),vlookup_a1_v,new_excel_number(2)).number == 10);
|
2001
|
+
assert(vlookup_3(new_excel_number(0.5),vlookup_a1_v,new_excel_number(2)).type == ExcelError);
|
2002
|
+
assert(vlookup_3(new_excel_number(10),vlookup_a1_v,new_excel_number(2)).number == 30);
|
2003
|
+
assert(vlookup_3(new_excel_number(2.6),vlookup_a1_v,new_excel_number(2)).number == 20);
|
2004
|
+
// ... has a four argument variant that matches the lookup type
|
2005
|
+
assert(vlookup(new_excel_number(2.6),vlookup_a1_v,new_excel_number(2),TRUE).number == 20);
|
2006
|
+
assert(vlookup(new_excel_number(2.6),vlookup_a1_v,new_excel_number(2),FALSE).type == ExcelError);
|
2007
|
+
assert(vlookup(new_excel_string("HELLO"),vlookup_a2_v,new_excel_number(2),FALSE).number == 10);
|
2008
|
+
assert(vlookup(new_excel_string("HELMP"),vlookup_a2_v,new_excel_number(2),TRUE).number == 10);
|
2009
|
+
// ... BLANK should not match with anything" do
|
2010
|
+
assert(vlookup_3(BLANK,vlookup_a3_v,new_excel_number(2)).type == ExcelError);
|
2011
|
+
// ... should return an error if an argument is an error" do
|
2012
|
+
assert(vlookup(VALUE,vlookup_a1_v,new_excel_number(2),FALSE).type == ExcelError);
|
2013
|
+
assert(vlookup(new_excel_number(2.0),VALUE,new_excel_number(2),FALSE).type == ExcelError);
|
2014
|
+
assert(vlookup(new_excel_number(2.0),vlookup_a1_v,VALUE,FALSE).type == ExcelError);
|
2015
|
+
assert(vlookup(new_excel_number(2.0),vlookup_a1_v,new_excel_number(2),VALUE).type == ExcelError);
|
2016
|
+
assert(vlookup(VALUE,VALUE,VALUE,VALUE).type == ExcelError);
|
2017
|
+
|
2018
|
+
// Test SUM
|
2019
|
+
ExcelValue sum_array_0[] = {new_excel_number(1084.4557258064517),new_excel_number(32.0516914516129),new_excel_number(137.36439193548387)};
|
2020
|
+
ExcelValue sum_array_0_v = new_excel_range(sum_array_0,3,1);
|
2021
|
+
ExcelValue sum_array_1[] = {sum_array_0_v};
|
2022
|
+
assert(sum(1,sum_array_1).number == 1253.8718091935484);
|
2023
|
+
|
2024
|
+
return 0;
|
2025
|
+
}
|
2026
|
+
|
2027
|
+
int main() {
|
2028
|
+
return test_functions();
|
2029
|
+
}
|