├── lib ├── trout.rb └── trout │ ├── version_list.rb │ ├── cli.rb │ └── managed_file.rb ├── .gitignore ├── .bundle └── config ├── bin └── trout ├── Gemfile ├── features ├── support │ └── env.rb ├── sync_nested_file.feature ├── help.feature └── sync_gemfile.feature ├── Gemfile.lock ├── Rakefile ├── LICENSE ├── trout.gemspec └── README.textile /lib/trout.rb: -------------------------------------------------------------------------------- 1 | require 'trout/cli' 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | .swo 3 | *~ 4 | *.swp 5 | pkg 6 | 7 | -------------------------------------------------------------------------------- /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_DISABLE_SHARED_GEMS: "1" 3 | -------------------------------------------------------------------------------- /bin/trout: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'trout' 4 | Trout::CLI.run(ARGV) 5 | 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | gem "cucumber" 3 | gem "aruba" 4 | gem "rake" 5 | gem "rspec", :require => false 6 | 7 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'aruba' 3 | require 'fileutils' 4 | 5 | Before do 6 | FileUtils.rm_rf("tmp") 7 | FileUtils.mkdir("tmp") 8 | end 9 | 10 | PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze 11 | BIN_PATH = File.join(PROJECT_ROOT, 'bin').freeze 12 | LIB_PATH = File.join(PROJECT_ROOT, 'lib').freeze 13 | 14 | ENV['PATH'] = [BIN_PATH, ENV['PATH']].join(':') 15 | ENV['RUBYLIB'] = LIB_PATH 16 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | aruba (0.2.1) 5 | builder (2.1.2) 6 | cucumber (0.8.5) 7 | builder (~> 2.1.2) 8 | diff-lcs (~> 1.1.2) 9 | gherkin (~> 2.1.4) 10 | json_pure (~> 1.4.3) 11 | term-ansicolor (~> 1.0.4) 12 | diff-lcs (1.1.2) 13 | gherkin (2.1.5) 14 | trollop (~> 1.16.2) 15 | json_pure (1.4.6) 16 | rake (0.8.7) 17 | rspec (1.3.0) 18 | term-ansicolor (1.0.5) 19 | trollop (1.16.2) 20 | 21 | PLATFORMS 22 | ruby 23 | 24 | DEPENDENCIES 25 | aruba 26 | cucumber 27 | rake 28 | rspec 29 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | require 'date' 4 | require 'rake/gempackagetask' 5 | 6 | require 'cucumber/rake/task' 7 | 8 | desc 'Default: run the cucumber features.' 9 | task :default => :cucumber 10 | 11 | desc "Clean files generated by rake tasks" 12 | task :clobber => [:clobber_rdoc, :clobber_rcov] 13 | 14 | Cucumber::Rake::Task.new(:cucumber) do |t| 15 | t.fork = true 16 | t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'progress')] 17 | end 18 | 19 | eval("$specification = begin; #{IO.read('trout.gemspec')}; end") 20 | Rake::GemPackageTask.new($specification) do |package| 21 | package.need_zip = true 22 | package.need_tar = true 23 | end 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Joe Ferris 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /lib/trout/version_list.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'trout/managed_file' 3 | 4 | module Trout 5 | class VersionList 6 | FILE_VERSION = '1.0' 7 | 8 | attr_accessor :path, :data, :files 9 | 10 | def initialize(path) 11 | @path = path 12 | end 13 | 14 | def [](filename) 15 | read 16 | attributes = files[filename] || { :filename => filename } 17 | ManagedFile.new(attributes) 18 | end 19 | 20 | def []=(filename, managed_file) 21 | read 22 | files[filename] = managed_file.to_hash 23 | write 24 | end 25 | 26 | def <<(managed_file) 27 | self[managed_file.filename] = managed_file 28 | end 29 | 30 | private 31 | 32 | def read 33 | if File.exist?(path) 34 | self.data = YAML.load(IO.read(path)) 35 | else 36 | self.data = { :files => {}, 37 | :version => FILE_VERSION } 38 | end 39 | self.files = data[:files] 40 | end 41 | 42 | def write 43 | File.open(path, 'w') do |file| 44 | file.write(YAML.dump(data)) 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /trout.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = %q{trout} 3 | s.version = '0.3' 4 | s.summary = %q{So your common files can swim upstream.} 5 | s.description = %q{Trout allows you to maintain a base version of special 6 | files (like Gemfile) in one repository, and then syncronize just that 7 | file with several other repositories. This means that you can update your 8 | Gemfile in the master repository, and then get the latest of all the 9 | common gems that you use in each project just by running "trout update 10 | Gemfile".} 11 | 12 | s.files = Dir['[A-Z]*', 'lib/**/*.rb', 'features/**/*', 'bin/**/*'] 13 | s.require_path = 'lib' 14 | s.test_files = Dir['features/**/*'] 15 | 16 | s.default_executable = 'trout' 17 | s.executables = ['trout'] 18 | 19 | s.has_rdoc = false 20 | 21 | s.authors = ["Joe Ferris"] 22 | s.email = %q{jferris@thoughtbot.com} 23 | s.homepage = "http://github.com/jferris/trout" 24 | 25 | s.add_development_dependency('cucumber') 26 | s.add_development_dependency('aruba') 27 | 28 | s.platform = Gem::Platform::RUBY 29 | s.rubygems_version = %q{1.2.0} 30 | end 31 | 32 | -------------------------------------------------------------------------------- /features/sync_nested_file.feature: -------------------------------------------------------------------------------- 1 | Feature: sync a file nested within a directory the source repo 2 | 3 | Scenario: sync a nested file 4 | Given a directory named "upstream_repo" 5 | And a directory named "child_repo" 6 | And a file named "upstream_repo/sub/dir/file" with: 7 | """ 8 | contents 9 | """ 10 | When I cd to "upstream_repo" 11 | And I run "git init" 12 | And I run "git add sub" 13 | And I run "git commit -m 'Added file'" 14 | And I cd to "../child_repo" 15 | And I run "trout checkout --source-root sub/dir file ../upstream_repo" 16 | Then the output should contain: 17 | """ 18 | Checked out file from ../upstream_repo. 19 | """ 20 | And I run "cat file" 21 | Then the output should contain: 22 | """ 23 | contents 24 | """ 25 | When I cd to "../upstream_repo" 26 | And I write to "sub/dir/file" with: 27 | """ 28 | new contents 29 | """ 30 | When I run "git add sub" 31 | And I run "git commit -m 'updated file'" 32 | And I cd to "../child_repo" 33 | When I run "trout update file" 34 | Then the output should contain: 35 | """ 36 | Merged changes to file. 37 | """ 38 | When I run "cat file" 39 | Then the output should contain: 40 | """ 41 | new contents 42 | """ 43 | 44 | -------------------------------------------------------------------------------- /features/help.feature: -------------------------------------------------------------------------------- 1 | Feature: get help on using trout 2 | 3 | Scenario Outline: Specify an unknown command 4 | When I run "trout " 5 | Then the output should contain: 6 | """ 7 | I don't know how to "." 8 | Run "trout help" for usage information. 9 | """ 10 | Examples: 11 | | arguments | command | 12 | | swim | swim | 13 | | help swim | swim | 14 | 15 | Scenario Outline: Run an invalid command 16 | When I run "" 17 | Then the output should contain: 18 | """ 19 | I don't understand the options you provided. 20 | Run "" for usage information. 21 | """ 22 | Examples: 23 | | invalid command | help command | 24 | | trout checkout | trout help checkout | 25 | | trout checkout file | trout help checkout | 26 | | trout checkout file url extra | trout help checkout | 27 | | trout update | trout help update | 28 | | trout update file extra | trout help update | 29 | 30 | Scenario Outline: Ask for help 31 | When I run "" 32 | Then the output should contain: 33 | """ 34 | trout helps you sync individual files from other git repositories. 35 | """ 36 | And the output should contain: 37 | """ 38 | Commands: 39 | """ 40 | And the output should not contain: 41 | """ 42 | I don't know how 43 | """ 44 | Examples: 45 | | help command | 46 | | trout | 47 | | trout help | 48 | | trout -h | 49 | | trout --help | 50 | 51 | Scenario Outline: Ask for usage for a particular command 52 | When I run "trout help " 53 | Then the output should contain: 54 | """ 55 | Usage: trout 56 | """ 57 | Examples: 58 | | command | usage | 59 | | checkout | filename git_url | 60 | | update | filename | 61 | -------------------------------------------------------------------------------- /features/sync_gemfile.feature: -------------------------------------------------------------------------------- 1 | Feature: sync a Gemfile between two repositories 2 | 3 | Scenario: sync a Gemfile 4 | Given a directory named "upstream_repo" 5 | And a directory named "child_repo" 6 | And a file named "upstream_repo/Gemfile" with: 7 | """ 8 | source "http://rubygems.org" 9 | gem "rails" 10 | gem "mysql" 11 | """ 12 | When I cd to "upstream_repo" 13 | And I run "git init" 14 | And I run "git add Gemfile" 15 | And I run "git commit -m 'Added gemfile'" 16 | And I cd to "../child_repo" 17 | And I run "trout checkout Gemfile ../upstream_repo" 18 | Then the output should contain: 19 | """ 20 | Checked out Gemfile from ../upstream_repo. 21 | """ 22 | And I run "cat Gemfile" 23 | Then the output should contain: 24 | """ 25 | source "http://rubygems.org" 26 | gem "rails" 27 | gem "mysql" 28 | """ 29 | When I cd to "../upstream_repo" 30 | And I write to "Gemfile" with: 31 | """ 32 | source "http://rubygems.org" 33 | gem "rails" 34 | gem "postgresql" 35 | """ 36 | When I run "git add Gemfile" 37 | And I run "git commit -m 'Changed to postgres'" 38 | And I cd to "../child_repo" 39 | When I append to "Gemfile" with: 40 | """ 41 | 42 | gem "redcloth" 43 | """ 44 | When I run "trout update Gemfile" 45 | Then the output should contain: 46 | """ 47 | Merged changes to Gemfile. 48 | """ 49 | When I run "cat Gemfile" 50 | Then the output should contain: 51 | """ 52 | source "http://rubygems.org" 53 | gem "rails" 54 | <<<<<<< Gemfile 55 | gem "mysql" 56 | gem "redcloth" 57 | ======= 58 | gem "postgresql" 59 | >>>>>>> /tmp/upstream 60 | """ 61 | When I write to "Gemfile" with: 62 | """ 63 | source "http://rubygems.org" 64 | gem "rails" 65 | gem "mysql" 66 | gem "redcloth" 67 | """ 68 | When I run "trout update Gemfile" 69 | Then the output should contain: 70 | """ 71 | Gemfile already up to date. 72 | """ 73 | And I run "cat Gemfile" 74 | Then the output should contain: 75 | """ 76 | source "http://rubygems.org" 77 | gem "rails" 78 | gem "mysql" 79 | gem "redcloth" 80 | """ 81 | 82 | -------------------------------------------------------------------------------- /lib/trout/cli.rb: -------------------------------------------------------------------------------- 1 | require 'optparse' 2 | require 'trout/managed_file' 3 | require 'trout/version_list' 4 | 5 | module Trout 6 | class CLI 7 | def self.run(arguments) 8 | new(arguments).run 9 | end 10 | 11 | attr_accessor :command, :option_parser, :arguments, :managed_files, :file_attributes 12 | 13 | def initialize(arguments) 14 | self.arguments = arguments 15 | self.file_attributes = {} 16 | self.option_parser = parse_options 17 | self.managed_files = VersionList.new('.trout') 18 | end 19 | 20 | def run 21 | case command 22 | when 'checkout' 23 | file_attributes[:filename] = next_argument 24 | file_attributes[:git_url] = next_argument 25 | file = ManagedFile.new(file_attributes) 26 | end_arguments 27 | file.checkout 28 | managed_files << file 29 | when 'update' 30 | file = managed_files[next_argument] 31 | end_arguments 32 | file.update 33 | managed_files << file 34 | when 'help', nil 35 | puts option_parser 36 | if arguments_left? 37 | puts 38 | usage_for(next_argument) 39 | end 40 | else 41 | unknown_command(command) 42 | end 43 | end 44 | 45 | private 46 | 47 | def parse_options 48 | option_parser = OptionParser.new do |parser| 49 | parser.banner = 50 | "trout helps you sync individual files from other git repositories.\n\n" + 51 | "Usage: trout [options] command arguments" 52 | 53 | parser.separator "" 54 | parser.separator "Options:" 55 | parser.separator "" 56 | 57 | parser.on("-s", "--source-root PATH", "Path to the file in the source repository") do |path| 58 | file_attributes[:source_root] = path 59 | end 60 | parser.on("-h", "--help", "View this help document") 61 | 62 | parser.separator "" 63 | parser.separator "Commands:" 64 | parser.separator "" 65 | parser.separator " checkout - start tracking a file from another repository" 66 | parser.separator " help - get usage details for a specific command" 67 | parser.separator " update - synchronize changes to a tracked file" 68 | end 69 | 70 | option_parser.parse!(arguments) 71 | 72 | if arguments_left? 73 | self.command = next_argument 74 | else 75 | self.command = 'help' 76 | end 77 | 78 | option_parser 79 | end 80 | 81 | def next_argument 82 | arguments.shift or invalid_arguments 83 | end 84 | 85 | def arguments_left? 86 | !arguments.empty? 87 | end 88 | 89 | def invalid_arguments 90 | puts "I don't understand the options you provided." 91 | puts %{Run "trout help #{command}" for usage information.} 92 | exit 93 | end 94 | 95 | def end_arguments 96 | invalid_arguments if arguments_left? 97 | end 98 | 99 | def usage_for(command) 100 | case command 101 | when 'checkout' 102 | puts "Usage: trout checkout filename git_url" 103 | when 'update' 104 | puts "Usage: trout update filename" 105 | else 106 | unknown_command(command) 107 | end 108 | end 109 | 110 | def unknown_command(command) 111 | puts %{I don't know how to "#{command}."} 112 | puts %{Run "trout help" for usage information.} 113 | end 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /lib/trout/managed_file.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | module Trout 4 | class ManagedFile 5 | attr_accessor :filename, :git_url, :version, :latest_version, :source_root 6 | 7 | def initialize(attributes) 8 | self.filename = attributes[:filename] 9 | self.git_url = attributes[:git_url] 10 | self.version = attributes[:version] 11 | self.source_root = attributes[:source_root] || '/' 12 | end 13 | 14 | def checkout 15 | clone_repository 16 | copy_to_destination 17 | puts "Checked out #{filename} from #{git_url}." 18 | ensure 19 | cleanup 20 | end 21 | 22 | def update 23 | clone_repository 24 | if up_to_date? 25 | puts "#{filename} already up to date." 26 | else 27 | merge_to_destination 28 | puts "Merged changes to #{filename}." 29 | end 30 | ensure 31 | cleanup 32 | end 33 | 34 | def to_hash 35 | { :filename => filename, 36 | :git_url => git_url, 37 | :version => version, 38 | :source_root => source_root } 39 | end 40 | 41 | private 42 | 43 | def clone_repository 44 | run_or_fail("git clone #{git_url} #{working('git')}") 45 | self.latest_version = checked_out_version 46 | end 47 | 48 | def copy_to_destination 49 | FileUtils.cp(working('git', source_path), filename) 50 | self.version = checked_out_version 51 | end 52 | 53 | def merge_to_destination 54 | upstream = working('upstream') 55 | at_last_update = working('at_last_update') 56 | merge = working('merge') 57 | 58 | FileUtils.cp(working('git', source_path), upstream) 59 | 60 | checkout_last_version 61 | FileUtils.cp(working('git', source_path), at_last_update) 62 | 63 | enforce_newline(upstream) 64 | enforce_newline(at_last_update) 65 | enforce_newline(filename) 66 | 67 | run("diff3 -mE #{filename} #{at_last_update} #{upstream} > #{merge}") 68 | FileUtils.mv(merge, filename) 69 | 70 | self.version = latest_version 71 | ensure 72 | FileUtils.rm_rf(upstream) 73 | FileUtils.rm_rf(at_last_update) 74 | end 75 | 76 | def cleanup 77 | FileUtils.rm_rf(working('git')) 78 | end 79 | 80 | def prepare_working_directory 81 | FileUtils.mkdir(working_root) 82 | end 83 | 84 | def checked_out_version 85 | git_command("rev-parse master") 86 | end 87 | 88 | def checkout_last_version 89 | git_command("checkout #{version}") 90 | end 91 | 92 | def git_command(command) 93 | run_or_fail("git --git-dir=#{working('git/.git')} --work-tree=#{working('git')} #{command}").strip 94 | end 95 | 96 | def up_to_date? 97 | version == latest_version 98 | end 99 | 100 | def working(*paths) 101 | File.join('/tmp', *paths) 102 | end 103 | 104 | def enforce_newline(path) 105 | if IO.read(path)[-1].chr != "\n" 106 | File.open(path, "a") { |file| file.puts } 107 | end 108 | end 109 | 110 | def run(command) 111 | `#{command} 2>&1` 112 | end 113 | 114 | def run_or_fail(command) 115 | output = run(command) 116 | unless $? == 0 117 | raise "Command failed with status #{$?}:\n#{command}\n#{output}" 118 | end 119 | output 120 | end 121 | 122 | def source_path 123 | File.join(source_root, filename) 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h1. Trout 2 | 3 | So your common files can swim upstream. 4 | 5 | h2. DEPRECATED 6 | 7 | This gem is now deprecated. We no longer use it, and therefore do not maintain it. 8 | 9 | If you have a use for it and wish to maintain it please "let us know":mailto:support@thoughtbot.com . 10 | 11 | h2. Synopsis 12 | 13 | Trout allows you to maintain a base version of special files (like Gemfile) in 14 | one repository, and then syncronize just that file with several other 15 | repositories. This means that you can update your Gemfile in the master 16 | repository, and then get the latest of all the common gems that you use in each 17 | project just by running "trout update Gemfile". 18 | 19 | h2. Installation 20 | 21 |
22 | gem install trout
23 | 
24 | 25 | h2. Usage 26 | 27 | Once you have a master repository set up containing the file you want to 28 | syncronize, you can check out that file anywhere using trout: 29 | 30 |
31 | trout checkout Gemfile git://github.com/someurl
32 | 
33 | 34 | If you update the file in your master repository and want to get the changes, 35 | you can use the update command: 36 | 37 |
38 | trout update Gemfile
39 | 
40 | 41 | If there are any conflicts, trout will help you resolve them by merging the two 42 | trees using diff3. 43 | 44 | h2. Why 45 | 46 | I wrote this primarily to manage Gemfiles between projects, hoping to avoid 47 | using an "ancestor" git repository that gets dumped into the git history of 48 | every new project. This also solves the "multiple inheritance" problem of git 49 | repositories if you want to manage different files from different sources. 50 | 51 | h2. Author 52 | 53 | Copyright 2010 Joe Ferris 54 | 55 |
56 |                                                 ;t                                                  
57 |                                               jfLLf                                                 
58 |                                              tLfGfGL                                                
59 |                                            tLjjEffGDt                                               
60 |                                           fjfjfffLfGL;                                              
61 |                                          GffffLLLLLGLGGGGLDL;                                       
62 |                                         .GfffLLGGGLDLfffffGLGEGGLGf                                 
63 |                                          :GGGDGLLLLGfLfffGLELEfLfLGLLG                              
64 |                                      ffDLLLDGLffDDjjjjjLGffEfLLfELGGLLGL,                           
65 |                                 tLfffGfjjLfffjjjjjjjjjjjjjLjDffLELffLLLfLDG                         
66 |                     jj:      ffLjjGtiittfitGifttfftttDtjtjGtDjjGjjjLfLjffffLG                       
67 |                     jDGj  GDjtjGi;;;t;tt,,it;;Liij;iiGtttttiftLtjjtjjjffffLfffL.                    
68 |                      ,fLfftjti,;:..:.:.:,G;,G,i;,;t;tijtititLtitjftjGttjjLDffLLLD                   
69 |                     tjfjL;,:..i.,. .: t::i,,j:,;;;;ii;;tii;fiiDitttGtitttfjjjfDfLG                  
70 |                   Lfffij....    .i .;:..;;j,;i,,i;;;;t;iGiiiDititiLijttftijttLjfGLGt                
71 |   ;Gf;          fLfjj,,:tt...::,:,::,G,,,fti;iiiti;iiititftjttttjtitiftjiftititfLGLDD               
72 |  fEKDGELt     LLDt;;;;,,,,t,i;ii;;;;i;,iittttttjtjjjtttttttjjttjjjjLjtttitiGtGtffiLGDD,             
73 |   DDGGGffL,ijLLii;jji;titifijjtiti;ifi;i;ij;ttt;tttijtttDtttjjttjjjfjjfjDjttitjGjGGGDEDD.           
74 |   KGDLffLLfLjiijiiijtttjjjjjttiti;ttt;;ttit,i;i;titittjttfttGitDttjjjjjttjjjtffjffjGLDDDDE:         
75 |   EGDLLEGLftLtLtiLjtLjjjjttittiiiiititittiiiittijititttjtitftttittjtttjffjfffjjfLLjLLLGGGEDD        
76 |   iDGLDDDGjjtEjjjtffjfjjjttjjittitttit;tiii;tttttitiijtiittjtttttttGttGjtjffjffLfifLffGGGDGEEKj     
77 |    DGGGGGLfLfjfjjfjjjtttjjjtjttjttjjttitiittiiittitttiiitttttjtjjtititjttjfLLffGtffLffLLLGGDGGGE    
78 |    GGEGLLffjjjfDjfjfjjjjfffffjjtttjttitititttttjttttitttttjjjtitjtfttttjfftGLLGffLLLGGLLtjfffGLLL.  
79 |     LDDGGLfLfffjjtjfLD. ;LLffLLfjjtjtttitittitttttittti;tttiitttttttttjjfffLLLLfjLGLLGLDLffjffLjtL: 
80 |     KDLEGGjfjfjjffG    ttjtLttjfLffjjtttttjttttjttttttjtttttiittttttttjjjLLLGLtLfLLLLLGEGGftLjjt;t  
81 |     jELDEfffjjffj      tttjjtjfitjfffjjjjjjjjjjjtjtttjtttttttttttjjtjjjfftfGLL;GLGLLLLGLf;tLj,G.    
82 |      GDDDLfffjfj       ,ittfjfit   GLfLLLLLLLLLffjttttttttttttttttttjjfjfjjfLGiGLGLLffj;,LjiiG      
83 |      KGKGDLffff         ;iitjff          .fGLGGGijjjjjjttttttttttttttjjfLff,LGjLGDGLLi,;fftDG       
84 |      DELLfjLLL          ,;ijff                DiftjjLffjjjjtttttttjjjjfffjD,itGfGLLfj:tLjtfG        
85 |      ;EfEELfLj          ,;itj                Ljitt EDfLGLffjjjjjjjjjjffLjjtD:fLGijftifjtt;j;t.t.:;t 
86 |       LfKfGDG           :;ij                .,tttfi      :fLLLLLfffffffLfftGGjfffEGGftfi;i;:iLjjit  
87 |       tfDffDf            :i                 ,;;itft           ,LLLLLGLfLLGfGGLi.,tKKDffLLGDDGLLL    
88 |        GLEfDf                                ;;itjj                  jGGGGDfLLGL.:tjGLLLG,          
89 |        DDGDGL                                ;;iijj                 tjLGGDDKKKKDLGt,: .             
90 |        jfGEj                                  ,;it:                 tjffLGDLKKKWKK                  
91 |         GEfL                                  .:;;                  tjjffLGjEKEEKKK                 
92 |         GLf,                                    .                   ;jjjjfLfEEEEEKW                 
93 |         .Lf                                                         .tjjjjfiDDEEEEK                 
94 |          L                                                           ttjjjf  LEEEE:                 
95 |                                                                       tjjjf    ;ED                  
96 |                                                                        ittj                         
97 |                                                                          :
98 | 
99 | --------------------------------------------------------------------------------