coopy 0.6.4.1 → 1.0.0

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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/CHANGELOG.md +7 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.md +22 -0
  7. data/README.md +59 -0
  8. data/Rakefile +4 -6
  9. data/coopy.gemspec +26 -0
  10. data/lib/coopy.rb +32 -175
  11. data/lib/coopy/alignment.rb +260 -0
  12. data/lib/coopy/bag.rb +17 -0
  13. data/lib/coopy/cell_info.rb +24 -0
  14. data/lib/coopy/change_type.rb +10 -0
  15. data/lib/coopy/compare_flags.rb +62 -0
  16. data/lib/coopy/compare_table.rb +327 -0
  17. data/lib/coopy/coopy.rb +22 -0
  18. data/lib/coopy/cross_match.rb +10 -0
  19. data/lib/coopy/csv_table.rb +51 -0
  20. data/lib/coopy/diff_render.rb +307 -0
  21. data/lib/coopy/index.rb +73 -0
  22. data/lib/coopy/index_item.rb +17 -0
  23. data/lib/coopy/index_pair.rb +72 -0
  24. data/lib/coopy/mover.rb +123 -0
  25. data/lib/coopy/ordering.rb +27 -0
  26. data/lib/coopy/row.rb +9 -0
  27. data/lib/coopy/simple_cell.rb +15 -0
  28. data/lib/coopy/simple_table.rb +144 -0
  29. data/lib/coopy/simple_view.rb +36 -0
  30. data/lib/coopy/table.rb +44 -0
  31. data/lib/coopy/table_comparison_state.rb +33 -0
  32. data/lib/coopy/table_diff.rb +634 -0
  33. data/lib/coopy/table_text.rb +14 -0
  34. data/lib/coopy/table_view.rb +31 -0
  35. data/lib/coopy/unit.rb +53 -0
  36. data/lib/coopy/version.rb +3 -0
  37. data/lib/coopy/view.rb +34 -0
  38. data/spec/fixtures/bridges.html +10 -0
  39. data/spec/fixtures/bridges_diff.csv +8 -0
  40. data/spec/fixtures/bridges_new.csv +9 -0
  41. data/spec/fixtures/bridges_old.csv +9 -0
  42. data/spec/fixtures/planetary_bodies.html +22 -0
  43. data/spec/fixtures/planetary_bodies_diff.csv +19 -0
  44. data/spec/fixtures/planetary_bodies_new.csv +20 -0
  45. data/spec/fixtures/planetary_bodies_old.csv +19 -0
  46. data/spec/fixtures/quote_me.csv +10 -0
  47. data/spec/fixtures/quote_me2.csv +11 -0
  48. data/spec/integration/table_diff_spec.rb +57 -0
  49. data/spec/libs/compare_flags_spec.rb +40 -0
  50. data/spec/libs/coopy_spec.rb +14 -0
  51. data/spec/libs/ordering_spec.rb +28 -0
  52. data/spec/libs/unit_spec.rb +31 -0
  53. data/spec/spec_helper.rb +29 -0
  54. metadata +153 -46
  55. data/bin/sqlite_diff +0 -4
  56. data/bin/sqlite_patch +0 -4
  57. data/bin/sqlite_rediff +0 -4
  58. data/lib/coopy/dbi_sql_wrapper.rb +0 -89
  59. data/lib/coopy/diff_apply_sql.rb +0 -35
  60. data/lib/coopy/diff_columns.rb +0 -33
  61. data/lib/coopy/diff_output.rb +0 -21
  62. data/lib/coopy/diff_output_action.rb +0 -34
  63. data/lib/coopy/diff_output_group.rb +0 -40
  64. data/lib/coopy/diff_output_raw.rb +0 -17
  65. data/lib/coopy/diff_output_stats.rb +0 -45
  66. data/lib/coopy/diff_output_table.rb +0 -49
  67. data/lib/coopy/diff_output_tdiff.rb +0 -48
  68. data/lib/coopy/diff_parser.rb +0 -92
  69. data/lib/coopy/diff_render_csv.rb +0 -29
  70. data/lib/coopy/diff_render_html.rb +0 -74
  71. data/lib/coopy/diff_render_log.rb +0 -52
  72. data/lib/coopy/row_change.rb +0 -25
  73. data/lib/coopy/scraperwiki_sql_wrapper.rb +0 -8
  74. data/lib/coopy/scraperwiki_utils.rb +0 -23
  75. data/lib/coopy/sequel_sql_wrapper.rb +0 -73
  76. data/lib/coopy/sql_compare.rb +0 -222
  77. data/lib/coopy/sql_wrapper.rb +0 -34
  78. data/lib/coopy/sqlite_sql_wrapper.rb +0 -143
  79. data/test/test_coopy.rb +0 -126
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7493ee11a18d6521e605f88a26b937ce6b096f15
4
+ data.tar.gz: a0410f887465a882377849fe96e55c1c70588708
5
+ SHA512:
6
+ metadata.gz: 37677ed77595308b7a205686c3048ba02051603ca8b6ce3c6ff1000f97e5c8522a243472cf01e5f8782273a9161fa81540d70f9fbe0f13b30179a6f35e7f16a1
7
+ data.tar.gz: 6806bd9e2ded7bedd32315c6764beac55338b7ae50b5c119609840504e87ebe52fbf45c4d956ba065749603148676204dba430d62f429f2eeb74ae76541c3c67
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1,7 @@
1
+ # Coopy for Ruby Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ * First working version!
6
+ * Basic support for two-way diffs of Ruby CSV objects
7
+ * HTML output
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ ruby '2.0.0'
4
+ #ruby-gemset=coopy
5
+
6
+ # Specify your gem's dependencies in coopy.gemspec
7
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 The Open Data Institute
2
+
3
+ # MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,59 @@
1
+ # Coopy for Ruby
2
+
3
+ [![Build Status](http://jenkins.theodi.org/job/coopy-ruby-master/badge/icon)](http://jenkins.theodi.org/job/coopy-ruby-master/)
4
+ [![Dependency Status](https://gemnasium.com/theodi/coopy-ruby.png)](https://gemnasium.com/theodi/coopy-ruby)
5
+ [![Code Climate](https://codeclimate.com/github/theodi/coopy-ruby.png)](https://codeclimate.com/github/theodi/coopy-ruby)
6
+
7
+ A pure Ruby port of Paul Fitzpatrick's [coopyhx](http://paulfitz.github.io/coopyhx) library for tabular diffs.
8
+
9
+ Not all the coopyhx code is ported or tested. There will be bugs. However, basic two-file CSV diff appears to be working. See 'Usage' section below for details.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'coopy'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install coopy
24
+
25
+ ## Usage
26
+
27
+ You can diff Ruby's built-in CSV objects, like so:
28
+
29
+ ```
30
+ old_table = Coopy::CsvTable.new(old_csv_object)
31
+ new_table = Coopy::CsvTable.new(new_csv_object)
32
+
33
+ alignment = Coopy.compare_tables(old_table,new_table).align
34
+ flags = Coopy::CompareFlags.new
35
+ highlighter = Coopy::TableDiff.new(alignment,flags)
36
+
37
+ diff_table = Coopy::SimpleTable.new(0,0)
38
+ highlighter.hilite diff_table
39
+ ```
40
+
41
+ You can inspect `diff_table` to see the changes.
42
+
43
+ You can also generate an HTML view of this diff like this:
44
+
45
+ ```
46
+ diff2html = Coopy::DiffRender.new
47
+ diff2html.render diff_table
48
+ html = diff2html.html
49
+ ```
50
+
51
+ There is plenty more in the original coopyhx, but this is all that's known working at the moment.
52
+
53
+ ## Contributing
54
+
55
+ 1. Fork it
56
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
57
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
58
+ 4. Push to the branch (`git push origin my-new-feature`)
59
+ 5. Create new Pull Request
data/Rakefile CHANGED
@@ -1,8 +1,6 @@
1
- require 'rake/testtask'
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
2
3
 
3
- Rake::TestTask.new do |t|
4
- t.libs << 'test'
5
- end
4
+ RSpec::Core::RakeTask.new(:spec)
6
5
 
7
- desc "Run tests"
8
- task :default => :test
6
+ task :default => :spec
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'coopy/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "coopy"
8
+ spec.version = Coopy::VERSION
9
+ spec.authors = ["James Smith"]
10
+ spec.email = ["james@floppy.org.uk"]
11
+ spec.description = %q{Ruby port of coopyhx, for calculating tabular diffs}
12
+ spec.summary = %q{Ruby port of coopyhx, for calculating tabular diffs}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "simplecov-rcov"
25
+ spec.add_development_dependency "pry"
26
+ end
@@ -1,178 +1,35 @@
1
- require 'optparse'
1
+ require "coopy/version"
2
2
 
3
- class Coopy
4
-
5
- class Flavor
6
- attr_accessor :key
7
- attr_accessor :banner
8
- attr_accessor :min_length
9
-
10
- def sql_subject?
11
- [:diff,:patch].include? key
12
- end
13
-
14
- def sql_object?
15
- key == :diff
16
- end
17
-
18
- def can_choose_format?
19
- key != :patch
20
- end
21
-
22
- def can_set_output?
23
- key != :patch
24
- end
25
-
26
- def default_format
27
- (key==:patch) ? :apply : :csv
28
- end
29
- end
30
-
31
- class OpenStruct
32
- attr_accessor :format
33
- attr_accessor :output
34
- end
35
-
36
- def self.parse(flavor,args)
37
- options = OpenStruct.new
38
- options.format = flavor.default_format
39
- options.output = nil
40
- OptionParser.new do |opts|
41
- begin
42
- opts.banner = flavor.banner
43
- opts.separator ""
44
- opts.separator "Specific options"
45
- if flavor.can_choose_format?
46
- opts.on("-f","--format [FORMAT]", [:csv, :html, :tdiff, :apply, :stats],
47
- "select format (csv,html,tdiff,apply,stats)") do |fmt|
48
- options.format = fmt
49
- end
50
- end
51
- if flavor.can_set_output?
52
- opts.on("-o", "--output [FILENAME]",
53
- "direct output to a file") do |fname|
54
- options.output = fname
55
- end
56
- end
57
- opts.on_tail("-h", "--help", "Show this message") do
58
- puts opts
59
- exit
60
- end
61
- opts.parse!(args)
62
- return options
63
- rescue
64
- puts "#{$!} (--help for help)"
65
- exit 1
66
- end
67
- end
68
- end
69
-
70
- def self.core(flavor,argv)
71
- options = self.parse(flavor,argv)
72
-
73
- if argv.length < flavor.min_length
74
- self.parse(flavor,["--help"])
75
- exit(1)
76
- end
77
-
78
- if flavor.sql_subject?
79
- db = SQLite3::Database.new(argv[0])
80
- sql = SqliteSqlWrapper.new(db)
81
- end
82
-
83
- if flavor.sql_object?
84
- name1 = nil
85
- name2 = nil
86
- case argv.length
87
- when 2
88
- name0 = sql.get_table_names[0]
89
- db.execute("ATTACH ? AS `__peer_ - _`",argv[1])
90
- name1 = "main.#{name0}"
91
- name2 = "__peer_ - _.#{name0}"
92
- when 3
93
- name1 = argv[1]
94
- name2 = argv[2]
95
- when 4
96
- name1 = "main.#{argv[1]}"
97
- db.execute("ATTACH ? AS __peer__",argv[2])
98
- name2 = "__peer__.#{argv[3]}"
99
- end
100
- cmp = SqlCompare.new(sql,name1,name2)
101
- else
102
- cmp = DiffParser.new(argv[flavor.min_length-1])
103
- end
104
-
105
- patches = DiffOutputGroup.new
106
- # patches << DiffOutputRaw.new
107
- case options.format
108
- when :html
109
- patches << DiffRenderHtml.new
110
- when :tdiff
111
- patches << DiffOutputTdiff.new
112
- when :csv
113
- patches << DiffRenderCsv.new
114
- when :apply
115
- patches << DiffApplySql.new(sql,name1)
116
- when :raw
117
- patches << DiffOutputRaw.new
118
- when :stats
119
- patches << DiffOutputStats.new
120
- else
121
- patches << DiffRenderCsv.new
122
- end
123
-
124
- cmp.set_output(patches)
125
-
126
- cmp.apply
127
- result = patches.to_string
128
- if result != ""
129
- if options.output.nil?
130
- print result
131
- else
132
- File.open(options.output,"w") do |f|
133
- f << result
134
- end
135
- end
136
- end
137
- 0
138
- end
139
-
140
- def self.diff(argv)
141
- flavor = Flavor.new
142
- flavor.key = :diff
143
- flavor.banner = "Usage: sqlite_diff [options] ver1.sqlite ver2.sqlite"
144
- flavor.min_length = 2
145
- self.core(flavor,argv)
146
- end
147
-
148
- def self.patch(argv)
149
- flavor = Flavor.new
150
- flavor.key = :patch
151
- flavor.banner = "Usage: sqlite_patch [options] db.sqlite patch.csv"
152
- flavor.min_length = 2
153
- self.core(flavor,argv)
154
- end
155
-
156
- def self.rediff(argv)
157
- flavor = Flavor.new
158
- flavor.key = :rediff
159
- flavor.banner = "Usage: sqlite_rediff [options] patch.csv"
160
- flavor.min_length = 1
161
- self.core(flavor,argv)
162
- end
3
+ module Coopy
4
+ # Your code goes here...
163
5
  end
164
6
 
165
- require 'coopy/diff_output_raw'
166
- require 'coopy/diff_output_tdiff'
167
- require 'coopy/diff_render_html'
168
- require 'coopy/diff_render_csv'
169
- require 'coopy/diff_output_action'
170
- require 'coopy/diff_output_group'
171
- require 'coopy/diff_output_stats'
172
- require 'coopy/diff_apply_sql'
173
- require 'coopy/diff_parser'
174
-
175
- require 'coopy/sqlite_sql_wrapper'
176
- require 'coopy/sql_compare'
177
- require 'sqlite3'
178
-
7
+ # interfaces
8
+ require 'coopy/bag'
9
+ require 'coopy/row'
10
+ require 'coopy/table'
11
+ require 'coopy/view'
12
+
13
+ # proper classes
14
+ require 'coopy/compare_flags'
15
+ require 'coopy/change_type'
16
+ require 'coopy/cell_info'
17
+ require 'coopy/unit'
18
+ require 'coopy/ordering'
19
+ require 'coopy/simple_cell'
20
+ require 'coopy/index_item'
21
+ require 'coopy/index'
22
+ require 'coopy/cross_match'
23
+ require 'coopy/index_pair'
24
+ require 'coopy/alignment'
25
+ require 'coopy/table_view'
26
+ require 'coopy/table_comparison_state'
27
+ require 'coopy/compare_table'
28
+ require 'coopy/mover'
29
+ require 'coopy/table_diff'
30
+ require 'coopy/coopy'
31
+ require 'coopy/simple_view'
32
+ require 'coopy/csv_table'
33
+ require 'coopy/simple_table'
34
+ require 'coopy/table_text'
35
+ require 'coopy/diff_render'
@@ -0,0 +1,260 @@
1
+ module Coopy
2
+ class Alignment
3
+
4
+ attr_accessor :reference # Alignment
5
+ attr_accessor :meta # Alignment
6
+
7
+ def initialize
8
+ @map_a2b = {} # Map<Int,Int>
9
+ @map_b2a = {} # Map<Int,Int>
10
+ @ha = @hb = 0
11
+ @map_count = 0
12
+ @reference = nil
13
+ @meta = nil
14
+ @order_cache_has_reference = false
15
+ @ia = 0
16
+ @ib = 0
17
+ end
18
+
19
+ def range(ha, hb)
20
+ @ha = ha
21
+ @hb = hb
22
+ end
23
+
24
+ def tables(ta, tb)
25
+ @ta = ta
26
+ @tb = tb
27
+ end
28
+
29
+ def headers(ia, ib)
30
+ @ia = ia
31
+ @ib = ib
32
+ end
33
+
34
+ def set_rowlike(flag)
35
+ end
36
+
37
+ def hb
38
+ @hb
39
+ end
40
+
41
+ def link(a, b)
42
+ @map_a2b[a] = b
43
+ @map_b2a[b] = a
44
+ @map_count+=1
45
+ end
46
+
47
+ def add_index_columns(unit)
48
+ if @index_columns.nil?
49
+ @index_columns = []
50
+ end
51
+ @index_columns << unit
52
+ end
53
+
54
+ def get_index_columns
55
+ @index_columns
56
+ end
57
+
58
+ def a2b(a)
59
+ @map_a2b[a]
60
+ end
61
+
62
+ def b2a(b)
63
+ @map_b2a[b]
64
+ end
65
+
66
+ def count
67
+ @map_count
68
+ end
69
+
70
+ def to_s
71
+ map_a2b.to_s
72
+ end
73
+
74
+ def to_order
75
+ if @order_cache
76
+ if @reference
77
+ if !@order_cache_has_reference
78
+ @order_cache = nil
79
+ end
80
+ end
81
+ end
82
+ @order_cache = to_order_3 if @order_cache.nil?
83
+ @order_cache_has_reference = true if reference
84
+ @order_cache
85
+ end
86
+
87
+ def get_source
88
+ @ta
89
+ end
90
+
91
+ def get_target
92
+ @tb
93
+ end
94
+
95
+ def get_source_header
96
+ @ia
97
+ end
98
+
99
+ def get_target_header
100
+ @ib
101
+ end
102
+
103
+ def to_order_3
104
+ ref = @reference
105
+ if ref.nil?
106
+ ref = Coopy::Alignment.new
107
+ ref.range(@ha,@ha)
108
+ ref.tables(@ta,@ta)
109
+ (0...@ha).each do |i|
110
+ ref.link(i,i)
111
+ end
112
+ end
113
+ order = Coopy::Ordering.new
114
+ if @reference.nil?
115
+ order.ignore_parent
116
+ end
117
+ xp = 0
118
+ xl = 0
119
+ xr = 0
120
+ hp = @ha
121
+ hl = ref.hb
122
+ hr = @hb
123
+ vp = {}
124
+ vl = {}
125
+ vr = {}
126
+ (0...hp).each { |i| vp[i] = i }
127
+ (0...hl).each { |i| vl[i] = i }
128
+ (0...hr).each { |i| vr[i] = i }
129
+ ct_vp = hp
130
+ ct_vl = hl
131
+ ct_vr = hr
132
+ prev = -1
133
+ ct = 0
134
+ max_ct = (hp+hl+hr)*10
135
+ while (ct_vp>0 ||
136
+ ct_vl>0 ||
137
+ ct_vr>0) do
138
+ ct+=1
139
+ if ct>max_ct
140
+ puts("Ordering took too long, something went wrong")
141
+ break
142
+ end
143
+ xp = 0 if (xp>=hp)
144
+ xl = 0 if (xl>=hl)
145
+ xr = 0 if (xr>=hr)
146
+ if xp<hp && ct_vp>0
147
+ if a2b(xp).nil? && ref.a2b(xp).nil?
148
+ if vp.has_key?(xp)
149
+ order.add(-1,-1,xp)
150
+ prev = xp
151
+ vp.remove(xp)
152
+ ct_vp-=1
153
+ end
154
+ xp+=1
155
+ next
156
+ end
157
+ end
158
+ zl = nil
159
+ zr = nil
160
+ if xl<hl && ct_vl>0
161
+ zl = ref.b2a(xl)
162
+ if zl.nil?
163
+ if vl.has_key?(xl)
164
+ order.add(xl,-1,-1)
165
+ vl.remove(xl)
166
+ ct_vl-=1
167
+ end
168
+ xl+=1
169
+ next
170
+ end
171
+ end
172
+ if xr<hr && ct_vr>0
173
+ zr = b2a(xr)
174
+ if zr.nil?
175
+ if vr.has_key?(xr)
176
+ order.add(-1,xr,-1)
177
+ vr.delete(xr)
178
+ ct_vr-=1
179
+ end
180
+ xr+=1
181
+ next
182
+ end
183
+ end
184
+ if zl
185
+ if a2b(zl).nil?
186
+ # row deleted in remote
187
+ if vl.has_key?(xl)
188
+ order.add(xl,-1,zl)
189
+ prev = zl
190
+ vp.delete(zl)
191
+ ct_vp-=1
192
+ vl.delete(xl)
193
+ ct_vl-=1
194
+ xp = zl+1
195
+ end
196
+ xl+=1
197
+ next
198
+ end
199
+ end
200
+ if zr
201
+ if ref.a2b(zr).nil?
202
+ # row deleted in local
203
+ if vr.has_key?(xr)
204
+ order.add(-1,xr,zr)
205
+ prev = zr
206
+ vp.remove(zr)
207
+ ct_vp-=1
208
+ vr.remove(xr)
209
+ ct_vr-=1
210
+ xp = zr+1
211
+ end
212
+ xr+=1
213
+ next
214
+ end
215
+ end
216
+ if zl && zr && a2b(zl) && ref.a2b(zr)
217
+ # we have a choice of order
218
+ # local thinks zl should come next
219
+ # remote thinks zr should come next
220
+ if zl==prev+1 || zr!=prev+1
221
+ if vr.has_key?(xr)
222
+ order.add(ref.a2b(zr),xr,zr)
223
+ prev = zr
224
+ vp.delete(zr)
225
+ ct_vp-=1
226
+ vl.delete(ref.a2b(zr))
227
+ ct_vl-=1
228
+ vr.delete(xr)
229
+ ct_vr-=1
230
+ xp = zr+1
231
+ xl = ref.a2b(zr)+1
232
+ end
233
+ xr+=1
234
+ next
235
+ else
236
+ if vl.has_key?(xl)
237
+ order.add(xl,a2b(zl),zl)
238
+ prev = zl
239
+ vp.remove(zl)
240
+ ct_vp-=1
241
+ vl.remove(xl)
242
+ ct_vl-=1
243
+ vr.remove(a2b(zl))
244
+ ct_vr-=1
245
+ xp = zl+1
246
+ xr = a2b(zl)+1
247
+ end
248
+ xl+=1
249
+ next
250
+ end
251
+ end
252
+ xp+=1
253
+ xl+=1
254
+ xr+=1
255
+ end
256
+ return order
257
+ end
258
+
259
+ end
260
+ end