excel_to_code 0.1.13 → 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
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