excel_to_code 0.1.13 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5a9204f1b847531abe1231be586e07efde9d6f56
4
- data.tar.gz: 55e3970859d6b607b877e91c4670777c098e5d78
3
+ metadata.gz: 37491ae4c70dc1d7a26a9d2487bb5e993791eb4b
4
+ data.tar.gz: ac3feebc2f5dcff782cd919d125146ad18a0865a
5
5
  SHA512:
6
- metadata.gz: 994609e98432c12a0abe413587fe7cefa9c138aedd43e174c1ff54a349bf716ed7e82e05413fab9f94b5291df2a1ff89881226c1559593a9052ff20c204ef9ef
7
- data.tar.gz: 83139c0c54325b3303c64065b07d18a529a2a761de1391932388c41dc038d51c152bff100bf50c9e50066962eb5e45c136b996bcfba764f2fe025f2f0eb6a9c8
6
+ metadata.gz: 8d92241f090a56b107cfc75f3f066a1ede7cfec0f9b246f824702eb55749b52cd8de42b1f529b249c52a74e48b0698494f5c04623394a03a120d4feff3d74d99
7
+ data.tar.gz: c16f7d74c3986ddd28cf83def0453ce5efa31e983b6d548e5c789aa79782bc236fdc5b4dd48233f1894fdb2b87e0f3ed16770e56f875c8f94aca156d613c2f32
@@ -564,6 +564,9 @@ class ExcelToX
564
564
  replace r, [name, 'Formulae'], "Workbook tables", [name, 'Formulae']
565
565
 
566
566
  replace ReplaceRangesWithArrayLiterals, [name, 'Formulae'], [name, 'Formulae']
567
+ replace ReplaceArithmeticOnRanges, [name, 'Formulae'], [name, 'Formulae']
568
+ replace ReplaceArraysWithSingleCells, [name, 'Formulae'], [name, 'Formulae']
569
+ replace WrapFormulaeThatReturnArraysAndAReNotInArrays, [name, 'Formulae'], [name, 'Formulae']
567
570
  end
568
571
  end
569
572
 
@@ -612,7 +615,9 @@ class ExcelToX
612
615
 
613
616
  # The result of the indirect might be a range, which we need to simplify
614
617
  replace ReplaceRangesWithArrayLiterals, [name, 'Formulae'], [name, 'Formulae']
618
+ replace ReplaceArithmeticOnRanges, [name, 'Formulae'], [name, 'Formulae']
615
619
  replace ReplaceArraysWithSingleCells, [name, 'Formulae'], [name, 'Formulae']
620
+ replace WrapFormulaeThatReturnArraysAndAReNotInArrays, [name, 'Formulae'], [name, 'Formulae']
616
621
  end
617
622
  end
618
623
 
Binary file
@@ -67,6 +67,7 @@ static ExcelValue excel_log(ExcelValue number);
67
67
  static ExcelValue excel_log_2(ExcelValue number, ExcelValue base);
68
68
  static ExcelValue max(int number_of_arguments, ExcelValue *arguments);
69
69
  static ExcelValue min(int number_of_arguments, ExcelValue *arguments);
70
+ static ExcelValue mmult(ExcelValue a_v, ExcelValue b_v);
70
71
  static ExcelValue mod(ExcelValue a_v, ExcelValue b_v);
71
72
  static ExcelValue negative(ExcelValue a_v);
72
73
  static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v);
@@ -1071,6 +1072,57 @@ static ExcelValue min(int number_of_arguments, ExcelValue *arguments) {
1071
1072
  return new_excel_number(smallest_number_found);
1072
1073
  }
1073
1074
 
1075
+ static ExcelValue mmult_error(ExcelValue a_v, ExcelValue b_v) {
1076
+ int rows = a_v.rows > b_v.rows ? a_v.rows : b_v.rows;
1077
+ int columns = a_v.columns > b_v.columns ? a_v.columns : b_v.columns;
1078
+ int i, j;
1079
+
1080
+ ExcelValue *result = (ExcelValue*) new_excel_value_array(rows*columns);
1081
+
1082
+ for(i=0; i<rows; i++) {
1083
+ for(j=0; j<columns; j++) {
1084
+ result[(i*columns) + j] = VALUE;
1085
+ }
1086
+ }
1087
+ return new_excel_range(result, rows, columns);
1088
+ }
1089
+
1090
+ static ExcelValue mmult(ExcelValue a_v, ExcelValue b_v) {
1091
+ CHECK_FOR_PASSED_ERROR(a_v)
1092
+ CHECK_FOR_PASSED_ERROR(b_v)
1093
+ if(a_v.type != ExcelRange) { return VALUE;}
1094
+ if(b_v.type != ExcelRange) { return VALUE;}
1095
+ if(a_v.columns != b_v.rows) { return mmult_error(a_v, b_v); }
1096
+ int n = a_v.columns;
1097
+ int a_rows = a_v.rows;
1098
+ int a_columns = a_v.columns;
1099
+ int b_rows = b_v.rows;
1100
+ int b_columns = b_v.columns;
1101
+ ExcelValue *result = (ExcelValue*) new_excel_value_array(a_rows*b_columns);
1102
+ int i, j, k;
1103
+ double sum;
1104
+ ExcelValue *array_a = a_v.array;
1105
+ ExcelValue *array_b = b_v.array;
1106
+
1107
+ ExcelValue a;
1108
+ ExcelValue b;
1109
+
1110
+ for(i=0; i<a_rows; i++) {
1111
+ for(j=0; j<b_columns; j++) {
1112
+ sum = 0;
1113
+ for(k=0; k<n; k++) {
1114
+ a = array_a[(i*a_columns)+k];
1115
+ b = array_b[(k*b_columns)+j];
1116
+ if(a.type != ExcelNumber) { return mmult_error(a_v, b_v); }
1117
+ if(b.type != ExcelNumber) { return mmult_error(a_v, b_v); }
1118
+ sum = sum + (a.number * b.number);
1119
+ }
1120
+ result[(i*b_columns)+j] = new_excel_number(sum);
1121
+ }
1122
+ }
1123
+ return new_excel_range(result, a_rows, b_columns);
1124
+ }
1125
+
1074
1126
  static ExcelValue mod(ExcelValue a_v, ExcelValue b_v) {
1075
1127
  CHECK_FOR_PASSED_ERROR(a_v)
1076
1128
  CHECK_FOR_PASSED_ERROR(b_v)
@@ -2414,6 +2466,64 @@ int test_functions() {
2414
2466
  // Two argument variant allows LOG base to be specified
2415
2467
  assert(excel_log_2(new_excel_number(8),new_excel_number(2)).number == 3.0);
2416
2468
  assert(excel_log_2(new_excel_number(8),new_excel_number(0)).type == ExcelError);
2469
+
2470
+ // Test MMULT (Matrix multiplication)
2471
+ ExcelValue mmult_1[] = { ONE, TWO, THREE, FOUR};
2472
+ ExcelValue mmult_2[] = { FOUR, THREE, TWO, ONE};
2473
+ ExcelValue mmult_3[] = { ONE, TWO};
2474
+ ExcelValue mmult_4[] = { THREE, FOUR};
2475
+ ExcelValue mmult_5[] = { ONE, BLANK, THREE, FOUR};
2476
+
2477
+ ExcelValue mmult_1_v = new_excel_range( mmult_1, 2, 2);
2478
+ ExcelValue mmult_2_v = new_excel_range( mmult_2, 2, 2);
2479
+ ExcelValue mmult_3_v = new_excel_range( mmult_3, 1, 2);
2480
+ ExcelValue mmult_4_v = new_excel_range( mmult_4, 2, 1);
2481
+ ExcelValue mmult_5_v = new_excel_range( mmult_5, 2, 2);
2482
+
2483
+ // Treat the ranges as matrices and multiply them
2484
+ ExcelValue mmult_result_1_v = mmult(mmult_1_v, mmult_2_v);
2485
+ assert(mmult_result_1_v.type == ExcelRange);
2486
+ assert(mmult_result_1_v.rows == 2);
2487
+ assert(mmult_result_1_v.columns == 2);
2488
+ ExcelValue *mmult_result_1_a = mmult_result_1_v.array;
2489
+ assert(mmult_result_1_a[0].number == 8);
2490
+ assert(mmult_result_1_a[1].number == 5);
2491
+ assert(mmult_result_1_a[2].number == 20);
2492
+ assert(mmult_result_1_a[3].number == 13);
2493
+
2494
+ ExcelValue mmult_result_2_v = mmult(mmult_3_v, mmult_4_v);
2495
+ assert(mmult_result_2_v.type == ExcelRange);
2496
+ assert(mmult_result_2_v.rows == 1);
2497
+ assert(mmult_result_2_v.columns == 1);
2498
+ ExcelValue *mmult_result_2_a = mmult_result_2_v.array;
2499
+ assert(mmult_result_2_a[0].number == 11);
2500
+
2501
+ // Return an error if any cells are not numbers
2502
+ ExcelValue mmult_result_3_v = mmult(mmult_5_v, mmult_2_v);
2503
+ assert(mmult_result_3_v.type == ExcelRange);
2504
+ assert(mmult_result_3_v.rows == 2);
2505
+ assert(mmult_result_3_v.columns == 2);
2506
+ ExcelValue *mmult_result_3_a = mmult_result_3_v.array;
2507
+ assert(mmult_result_3_a[0].type == ExcelError);
2508
+ assert(mmult_result_3_a[1].type == ExcelError);
2509
+ assert(mmult_result_3_a[2].type == ExcelError);
2510
+ assert(mmult_result_3_a[3].type == ExcelError);
2511
+
2512
+ // Returns errors if arguments are not ranges
2513
+ // FIXME: Should work in edge case where passed two numbers which excel treats as ranges with one row and column
2514
+ ExcelValue mmult_result_4_v = mmult(ONE, mmult_2_v);
2515
+ assert(mmult_result_4_v.type == ExcelError);
2516
+
2517
+ // Returns errors if the ranges aren't the right size to multiply
2518
+ ExcelValue mmult_result_5_v = mmult(mmult_1_v, mmult_3_v);
2519
+ assert(mmult_result_5_v.type == ExcelRange);
2520
+ assert(mmult_result_5_v.rows == 2);
2521
+ assert(mmult_result_5_v.columns == 2);
2522
+ ExcelValue *mmult_result_5_a = mmult_result_5_v.array;
2523
+ assert(mmult_result_5_a[0].type == ExcelError);
2524
+ assert(mmult_result_5_a[1].type == ExcelError);
2525
+ assert(mmult_result_5_a[2].type == ExcelError);
2526
+ assert(mmult_result_5_a[3].type == ExcelError);
2417
2527
 
2418
2528
  // Release memory
2419
2529
  free_all_allocated_memory();
@@ -53,6 +53,7 @@ class MapFormulaeToC < MapValuesToC
53
53
  'MATCH3' => 'excel_match',
54
54
  'MAX' => 'max',
55
55
  'MIN' => 'min',
56
+ 'MMULT' => 'mmult',
56
57
  'MOD' => 'mod',
57
58
  'PMT' => 'pmt',
58
59
  'PV3' => 'pv_3',
@@ -37,6 +37,7 @@ class MapFormulaeToRuby < MapValuesToRuby
37
37
  'MAX' => 'max',
38
38
  'MID' => 'mid',
39
39
  'MIN' => 'min',
40
+ 'MMULT' => 'mmult',
40
41
  'MOD' => 'mod',
41
42
  'PI' => 'pi',
42
43
  'PMT' => 'pmt',
@@ -83,3 +83,5 @@ require_relative 'excel_functions/text'
83
83
  require_relative 'excel_functions/hlookup'
84
84
 
85
85
  require_relative 'excel_functions/log'
86
+
87
+ require_relative 'excel_functions/mmult'
@@ -0,0 +1,26 @@
1
+ module ExcelFunctions
2
+
3
+ def mmult(array_a, array_b)
4
+ return array_a if array_a.is_a?(Symbol)
5
+ return array_b if array_b.is_a?(Symbol)
6
+ return :value unless array_a.is_a?(Array)
7
+ return :value unless array_b.is_a?(Array)
8
+
9
+ columns = array_a.first.length
10
+ rows = array_b.length
11
+ error = Array.new([rows, columns].max) { Array.new([rows, columns].max, :value) }
12
+ return error unless columns == rows
13
+ return error unless array_a.all? { |a| a.all? { |b| b.is_a?(Numeric) }}
14
+ return error unless array_b.all? { |a| a.all? { |b| b.is_a?(Numeric) }}
15
+ result = Array.new(array_a.length) { Array.new(array_b.first.length) }
16
+ indices = (0...rows).to_a
17
+ result.map.with_index do |row, i|
18
+ row.map.with_index do |cell, j|
19
+ indices.inject(0) do |sum, n|
20
+ sum = sum + (array_a[i][n] * array_b[n][j])
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ end
@@ -64,7 +64,7 @@ class AstExpandArrayFormulae
64
64
  end]
65
65
  end
66
66
 
67
- FUNCTIONS_THAT_ACCEPT_RANGES_FOR_ALL_ARGUMENTS = %w{AVERAGE COUNT COUNTA MAX MIN SUM SUMPRODUCT}
67
+ FUNCTIONS_THAT_ACCEPT_RANGES_FOR_ALL_ARGUMENTS = %w{AVERAGE COUNT COUNTA MAX MIN SUM SUMPRODUCT MMULT}
68
68
 
69
69
  def function(name,*arguments)
70
70
  if FUNCTIONS_THAT_ACCEPT_RANGES_FOR_ALL_ARGUMENTS.include?(name)
@@ -82,9 +82,7 @@ class AstExpandArrayFormulae
82
82
  end
83
83
 
84
84
  def map_match(*args)
85
- $DEBUGN = true
86
85
  a = array_map args, 'MATCH', false, true, false
87
- $DEBUGN = false
88
86
  a
89
87
  end
90
88
 
@@ -30,7 +30,7 @@ class ExtractArrayFormulaForCell
30
30
  ast[@row_offset+1][@column_offset+1] # plus ones to skip tthe [:array,[:row,"cell"]] symbols
31
31
  end
32
32
 
33
- FUNCTIONS_THAT_CAN_RETURN_ARRAYS = %w{INDEX}
33
+ FUNCTIONS_THAT_CAN_RETURN_ARRAYS = %w{INDEX MMULT}
34
34
 
35
35
  def map_function(ast)
36
36
  return ast unless FUNCTIONS_THAT_CAN_RETURN_ARRAYS.include?(ast[1])
@@ -16,3 +16,5 @@ require_relative "simplify/replace_common_elements_in_formulae"
16
16
  require_relative "simplify/replace_arrays_with_single_cells"
17
17
  require_relative "simplify/replace_values_with_constants"
18
18
  require_relative "simplify/sort_into_calculation_order"
19
+ require_relative "simplify/replace_arithmetic_on_ranges"
20
+ require_relative "simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays"
@@ -0,0 +1,83 @@
1
+ class ReplaceArithmeticOnRangesAst
2
+
3
+ def map(ast)
4
+ return ast unless ast.is_a?(Array)
5
+ operator = ast[0]
6
+ if respond_to?(operator)
7
+ send(operator,*ast[1..-1])
8
+ else
9
+ [operator,*ast[1..-1].map {|a| map(a) }]
10
+ end
11
+ end
12
+
13
+ def arithmetic(left, operator, right)
14
+ if left.first == :array && right.first != :array
15
+ mapped_right = map(right)
16
+ array_map(left) do |cell|
17
+ [:arithmetic, map(cell), operator, mapped_right]
18
+ end
19
+ elsif left.first != :array && right.first == :array
20
+ mapped_left = map(left)
21
+ array_map(right) do |cell|
22
+ [:arithmetic, mapped_left, operator, map(cell)]
23
+ end
24
+ elsif left.first == :array && right.first == :array
25
+ left.map.with_index do |row, i|
26
+ if row == :array
27
+ row
28
+ else
29
+ row.map.with_index do |cell, j|
30
+ if cell == :row
31
+ cell
32
+ elsif i >= left.length || i >= right.length || j >= left.first.length || j >= right.first.length
33
+ [:error, "#VALUE!"]
34
+ else
35
+ [:arithmetic, map(left[i][j]), operator, map(right[i][j])]
36
+ end
37
+ end
38
+ end
39
+ end
40
+ else
41
+ [:arithmetic, map(left), operator, map(right)]
42
+ end
43
+ end
44
+
45
+ def array_map(array)
46
+ array.map do |row|
47
+ if row == :array
48
+ row
49
+ else
50
+ row.map do |column|
51
+ if column == :row
52
+ column
53
+ else
54
+ yield column
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+
64
+ class ReplaceArithmeticOnRanges
65
+
66
+ def self.replace(*args)
67
+ self.new.replace(*args)
68
+ end
69
+
70
+ def replace(input,output)
71
+ rewriter = ReplaceArithmeticOnRangesAst.new
72
+ input.each_line do |line|
73
+ # Looks to match lines with references
74
+ if line =~ /:arithmetic/ && line =~ /:array/
75
+ content = line.split("\t")
76
+ ast = eval(content.pop)
77
+ output.puts "#{content.join("\t")}\t#{rewriter.map(ast).inspect}"
78
+ else
79
+ output.puts line
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,28 @@
1
+ require_relative '../excel'
2
+
3
+ class WrapFormulaeThatReturnArraysAndAReNotInArrays
4
+
5
+ def self.replace(*args)
6
+ self.new.replace(*args)
7
+ end
8
+
9
+ def replace(input,output)
10
+
11
+ input.each_line do |line|
12
+ # Looks to match lines that contain formulae that return ranges, such as MMULT
13
+ if line =~ /"MMULT"/
14
+ content = line.split("\t")
15
+ ast = eval(content.pop)
16
+ if ast[0] == :function && ast[1] == "MMULT"
17
+ new_ast = [:function, "INDEX", ast, [:number, "1"], [:number, "1"]]
18
+ output.puts "#{content.join("\t")}\t#{new_ast.inspect}"
19
+ else
20
+ output.puts line
21
+ end
22
+ else
23
+ output.puts line
24
+ end
25
+ end
26
+ end
27
+
28
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: excel_to_code
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.13
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Counsell, Green on Black Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-19 00:00:00.000000000 Z
11
+ date: 2013-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubypeg
@@ -145,6 +145,7 @@ files:
145
145
  - src/excel/excel_functions/max.rb
146
146
  - src/excel/excel_functions/mid.rb
147
147
  - src/excel/excel_functions/min.rb
148
+ - src/excel/excel_functions/mmult.rb
148
149
  - src/excel/excel_functions/mod.rb
149
150
  - src/excel/excel_functions/more_than.rb
150
151
  - src/excel/excel_functions/more_than_or_equal.rb
@@ -212,6 +213,7 @@ files:
212
213
  - src/simplify/inline_formulae.rb
213
214
  - src/simplify/map_formulae_to_values.rb
214
215
  - src/simplify/remove_cells.rb
216
+ - src/simplify/replace_arithmetic_on_ranges.rb
215
217
  - src/simplify/replace_arrays_with_single_cells.rb
216
218
  - src/simplify/replace_column_with_column_number.rb
217
219
  - src/simplify/replace_common_elements_in_formulae.rb
@@ -225,6 +227,7 @@ files:
225
227
  - src/simplify/replace_values_with_constants.rb
226
228
  - src/simplify/simplify_arithmetic.rb
227
229
  - src/simplify/sort_into_calculation_order.rb
230
+ - src/simplify/wrap_formulae_that_return_arrays_and_are_not_in_arrays.rb
228
231
  - src/simplify.rb
229
232
  - src/util/not_supported_exception.rb
230
233
  - src/util/try.rb