aoc_rb 0.0.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c55b5953ec5f5609a93d35056a675811dd5777a71514b6f3ed90eb64223212a4
4
- data.tar.gz: 1cc0e2404ad5efb0a38b5f4870785089fbd3b9d9edf86f8134cb793f43b47d2b
3
+ metadata.gz: 9c58f8fae503e8530a04cfb7e3adffab28dda605946ddcd8ca5d3a1eda2efa88
4
+ data.tar.gz: f6801d662d3ca723f0d88ccf5eda4172f5c172219fbc2eebb99a21442035718c
5
5
  SHA512:
6
- metadata.gz: f1d9532728421583243011320330aeb9a5279627fd2c05b842ced50b92ff19650cbaf7e31aa336c71d35f80a6f1dc5e02060aafc9dfb6718cde52f519eb845de
7
- data.tar.gz: d13d273694aa76935fd809ee3c7a777dbb9ba3449b2a18b411b905c06dd7876961b78e539fa33c4906d7ceaf929c8831615f2d818593cf32900553f6c16be3ff
6
+ metadata.gz: 6a9646f1149312b0fa36e6de7c473cb34d83458fff21caf85987475848dfd9d3e75c9d8fade76305280b012780d2f91842c0da64b28008c57e164b7677aed4c6
7
+ data.tar.gz: f0e89fb9eaaee8d28110c94d67ce821309b57835ba3286670426290030ffb1c7ca218212f554c006a64a646971c5a2fdf5476cd48e84239392bf3dc491bac049
@@ -0,0 +1,35 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Tests
9
+
10
+ on:
11
+ push:
12
+ branches: [ main ]
13
+ pull_request:
14
+ branches: [ main ]
15
+
16
+ jobs:
17
+ test:
18
+
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ matrix:
22
+ ruby-version: ['2.6', '2.7', '3.0']
23
+
24
+ steps:
25
+ - uses: actions/checkout@v2
26
+ - name: Set up Ruby
27
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
28
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
29
+ # uses: ruby/setup-ruby@v1
30
+ uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
31
+ with:
32
+ ruby-version: ${{ matrix.ruby-version }}
33
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
34
+ - name: Run tests
35
+ run: bundle exec rake
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /Gemfile.lock
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
data/README.md CHANGED
@@ -1,39 +1,65 @@
1
1
  # AocRb
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/aoc_rb`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ [![Tests](https://github.com/pacso/aoc_rb/actions/workflows/tests.yml/badge.svg)](https://github.com/pacso/aoc_rb/actions/workflows/tests.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/aoc_rb.svg)](https://badge.fury.io/rb/aoc_rb)
4
5
 
5
- TODO: Delete this and the text above, and describe your gem
6
+ This gem provides a simple way to create a project for solving [Advent of Code](https://adventofcode.com) puzzles.
6
7
 
7
- ## Installation
8
+ It also provides some handy tools to automate your interactions with the Advent of Code website.
8
9
 
9
- Add this line to your application's Gemfile:
10
+ ## Getting Started
10
11
 
11
- ```ruby
12
- gem 'aoc_rb'
13
- ```
12
+ First of all, install the gem:
14
13
 
15
- And then execute:
14
+ $ gem install aoc_rb
16
15
 
17
- $ bundle install
16
+ Next, generate your Advent of Code project:
18
17
 
19
- Or install it yourself as:
18
+ $ aoc new my-project-name
19
+
20
+ This will generate a new project with the given name. After this you should be ready to go!
20
21
 
21
- $ gem install aoc_rb
22
+ $ cd my-project-name
23
+ $ aoc
24
+
25
+ Before running any commands, you must set up your `AOC_COOKIE` environment variable:
22
26
 
23
- ## Usage
27
+ $ cp .env-template .env
24
28
 
25
- TODO: Write usage instructions here
29
+ You'll need to log in to the [Advent of Code](https://adventofcode.com) website and then grab a copy of your session key from your cookies.
30
+ In Chrome you can do this via [chrome settings](chrome://settings/cookies/detail?site=adventofcode.com) by entering `chrome://settings/cookies/detail?site=adventofcode.com` in your address bar. Just click on `session` and copy your key from the `Content` section.
26
31
 
27
- ## Development
32
+ Now edit the new `.env` file, so that it looks like the following, replacing `ABCDE12345` with the session key you just copied:
28
33
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
34
+ AOC_COOKIE=ABCDE12345
30
35
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
36
+ You're now able to run all the `aoc` commands provided by this gem.
37
+
38
+ You'll see you have a few options when running the `aoc` command within your project.
32
39
 
33
- ## Contributing
40
+ `prep` will set everything up for a new daily puzzle. It defaults to the puzzle for today, but you can work with older puzzles by passing the year and day:
41
+
42
+ $ aoc prep 2017 12
43
+
44
+ This will generate a `challenges/2017/12` folder with an `input.txt` file (your puzzle input), a `part_1.md` file with the instructions for part 1, and a `solution.rb` file for you to use when implementing your solution.
45
+
46
+ In `spec/2017/12` there will be a `solution_spec.rb` which you can use to write tests for your solution. It's a good idea to use the examples provided in the `part_1.md` to test your solution!
34
47
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/aoc_rb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/aoc_rb/blob/master/CODE_OF_CONDUCT.md).
48
+ When you've got a working solution you can see what it generates by running:
49
+
50
+ $ aoc exec
51
+
52
+ Which runs todays solution. For previous days, just like with `prep`, you can provide a date:
53
+
54
+ $ aoc exec 2017 12
55
+
56
+ If you're happy with the output, you can respond to the prompt and `aoc` will submit your answer for you. If you're successful with part 1, it will automatically download the instructions for part 2 for you.
57
+
58
+ Rinse & repeat!
59
+
60
+ ## Contributing
36
61
 
62
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pacso/aoc_rb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/pacso/aoc_rb/blob/master/CODE_OF_CONDUCT.md).
37
63
 
38
64
  ## License
39
65
 
data/aoc_rb.gemspec CHANGED
@@ -26,10 +26,10 @@ Gem::Specification.new do |spec|
26
26
  spec.require_paths = ["lib"]
27
27
 
28
28
  spec.add_dependency "dotenv", "~> 2.7.6"
29
- spec.add_dependency "httparty", "~> 0.18.1"
30
- spec.add_dependency "thor", "~> 1.0.1"
29
+ spec.add_dependency "httparty", "~> 0.20.0"
30
+ spec.add_dependency "thor", "~> 1.1.0"
31
31
  spec.add_dependency "rspec", "~> 3.0"
32
- spec.add_dependency "nokogiri", "~> 1.10.10"
32
+ spec.add_dependency "nokogiri", "~> 1.12.5"
33
33
 
34
- spec.add_development_dependency "webmock", "~> 3.10.0"
34
+ spec.add_development_dependency "webmock", "~> 3.14.0"
35
35
  end
data/lib/aoc_rb/app.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "httparty"
2
2
  require 'dotenv/load'
3
+ require "fileutils"
3
4
  require "thor"
4
5
 
5
6
  require 'aoc_rb/aoc_api'
@@ -8,8 +9,16 @@ require 'aoc_rb/puzzle_input'
8
9
  require 'aoc_rb/puzzle_solution'
9
10
  require 'aoc_rb/puzzle_source'
10
11
 
11
- src_files = File.join(Dir.getwd, "challenges", "**", "*.rb")
12
+ shared_files = File.join(Dir.getwd, "challenges", "shared", "**", "*.rb")
13
+ Dir[shared_files].each do |file|
14
+ if File.exist? file
15
+ require file
16
+ else
17
+ puts "missing file #{file}"
18
+ end
19
+ end
12
20
 
21
+ src_files = File.join(Dir.getwd, "challenges", "20**", "**", "*.rb")
13
22
  Dir[src_files].each do |file|
14
23
  if File.exist? file
15
24
  require file
@@ -71,12 +80,12 @@ module AocRb
71
80
  method_option :day, aliases: "-d", type: :numeric, default: Time.now.day
72
81
 
73
82
  def exec(year = options[:year], day = options[:day])
74
- puzzle = AocRb::PuzzleSource.create_puzzle(year, day)
75
83
  input = AocRb::PuzzleInput.load(year, day)
84
+ puzzle = AocRb::PuzzleSource.create_puzzle(year, day, input)
76
85
 
77
86
  level = Puzzle.instructions_exist?(year, day, :part_2) ? 2 : 1
78
87
  puts "#{year} Day #{day}"
79
- solution = PuzzleSource.run_part("part #{level}") { puzzle.send("part_#{level}", input) }
88
+ solution = PuzzleSource.run_part("part #{level}") { puzzle.send("part_#{level}") }
80
89
 
81
90
  puts "Submit solution? #{solution} (y/N)"
82
91
  submit = STDIN.gets.chomp.downcase
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pathname'
4
+
3
5
  module AocRb
4
6
  module AppLoader
5
7
  extend self
data/lib/aoc_rb/cli.rb CHANGED
@@ -13,7 +13,7 @@ module AocRb
13
13
  project_dir = File.join(Dir.getwd, name)
14
14
  if File.exist?(project_dir)
15
15
  puts "ERROR: #{project_dir} already exists!"
16
- exit 0
16
+ exit -1
17
17
  end
18
18
 
19
19
  bin_dir = File.join(project_dir, "bin")
@@ -22,6 +22,12 @@ module AocRb
22
22
  FileUtils.mkdir_p bin_dir
23
23
  File.open(bin_path, "w") { |f| f.write(File.read(bin_template)) }
24
24
 
25
+ shared_dir = File.join(project_dir, "challenges", "shared")
26
+ solution_path = File.join(shared_dir, "solution.rb")
27
+ solution_template = File.join(File.dirname(__FILE__), "../../templates/solution_base.rb")
28
+ FileUtils.mkdir_p shared_dir
29
+ File.open(solution_path, "w") { |f| f.write(File.read(solution_template)) }
30
+
25
31
  spec_dir = File.join(project_dir, "spec")
26
32
  spec_helper_path = File.join(spec_dir, "spec_helper.rb")
27
33
  spec_helper_template = File.join(File.dirname(__FILE__), "../../templates/spec/spec_helper.rb")
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module AocRb
3
4
  module PuzzleInput
4
5
  extend self
@@ -6,9 +6,9 @@ module AocRb
6
6
 
7
7
  def submit(level, year, day, answer = nil, allow_waiting = true)
8
8
  if answer.nil?
9
- puzzle = PuzzleSource.create_puzzle(year, day)
10
9
  input = PuzzleInput.load(year, day)
11
- answer = level == 1 ? puzzle.part_1(input) : puzzle.part_2(input)
10
+ puzzle = PuzzleSource.create_puzzle(year, day, input)
11
+ answer = level == 1 ? puzzle.part_1 : puzzle.part_2
12
12
  end
13
13
 
14
14
  aoc_api = AocApi.new(ENV['AOC_COOKIE'])
@@ -5,10 +5,10 @@ module AocRb
5
5
  module PuzzleSource
6
6
  extend self
7
7
 
8
- def create_puzzle(year, day)
8
+ def create_puzzle(year, day, input)
9
9
  padded_day = Puzzle.padded(day)
10
10
  begin
11
- Module.const_get("Year#{year}").const_get("Day#{padded_day}").new
11
+ Module.const_get("Year#{year}").const_get("Day#{padded_day}").new(input)
12
12
  rescue NameError
13
13
  puts "There is no solution for this puzzle"
14
14
  end
@@ -1,3 +1,3 @@
1
1
  module AocRb
2
- VERSION = "0.0.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -1,12 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
  module Year<%= @year %>
3
- class Day<%= @day %>
4
- def part_1(input)
3
+ class Day<%= @day %> < Solution
4
+ # @input is available if you need the raw data input
5
+ # Call `data` to access either an array of the parsed data, or a single record for a 1-line input file
6
+
7
+ def part_1
5
8
  nil
6
9
  end
7
10
 
8
- def part_2(input)
11
+ def part_2
9
12
  nil
10
13
  end
14
+
15
+ private
16
+ # Processes each line of the input file and stores the result in the dataset
17
+ # def process_input(line)
18
+ # line.map(&:to_i)
19
+ # end
20
+
21
+ # Processes the dataset as a whole
22
+ # def process_dataset(set)
23
+ # set
24
+ # end
11
25
  end
12
26
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true %>
2
+
3
+ class Solution
4
+ def self.part_1(*input)
5
+ new(*input).part_1
6
+ end
7
+
8
+ def self.part_2(*input)
9
+ new(*input).part_2
10
+ end
11
+
12
+ def initialize(input)
13
+ @input = input
14
+ end
15
+
16
+ def data
17
+ @data ||= begin
18
+ processed = @input.lines(chomp: true).map do |line|
19
+ process_input line
20
+ end
21
+
22
+ processed.length == 1 ? processed.first : process_dataset(processed)
23
+ end
24
+ end
25
+
26
+ private
27
+ def process_input(line)
28
+ line
29
+ end
30
+
31
+ def process_dataset(set)
32
+ set
33
+ end
34
+ end
@@ -2,7 +2,6 @@
2
2
  require 'spec_helper'
3
3
 
4
4
  RSpec.describe Year<%= @year %>::Day<%= @day %> do
5
- let(:puzzle) { Year<%= @year %>::Day<%= @day %>.new }
6
5
  let(:input) { File.read(File.join(File.dirname(__FILE__), "../../../challenges/<%= @year %>/<%= @day.to_s.rjust(2, "0") %>/input.txt")) }
7
6
  let(:example_input) {
8
7
  <<~EOF
@@ -13,21 +12,21 @@ RSpec.describe Year<%= @year %>::Day<%= @day %> do
13
12
 
14
13
  describe "part 1" do
15
14
  it "returns nil for the example input" do
16
- expect(puzzle.part_1(example_input)).to eq(nil)
15
+ expect(described_class.part_1(example_input)).to eq(nil)
17
16
  end
18
17
 
19
18
  it "returns nil for my input" do
20
- expect(puzzle.part_1(input)).to eq(nil)
19
+ expect(described_class.part_1(input)).to eq(nil)
21
20
  end
22
21
  end
23
22
 
24
23
  describe "part 2" do
25
24
  it "returns nil for the example input" do
26
- expect(puzzle.part_2(example_input)).to eq(nil)
25
+ expect(described_class.part_2(example_input)).to eq(nil)
27
26
  end
28
27
 
29
28
  it "returns nil for my input" do
30
- expect(puzzle.part_2(input)).to eq(nil)
29
+ expect(described_class.part_2(input)).to eq(nil)
31
30
  end
32
31
  end
33
32
  end
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Dir[File.join(File.dirname(__FILE__), "..", "challenges", "**", "*.rb")].each do |file|
3
+ Dir[File.join(File.dirname(__FILE__), "..", "challenges", "shared", "**", "*.rb")].each do |file|
4
+ require file
5
+ end
6
+
7
+ Dir[File.join(File.dirname(__FILE__), "..", "challenges", "20*", "**", "*.rb")].each do |file|
4
8
  require file
5
9
  end
6
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aoc_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Pascoe
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-02 00:00:00.000000000 Z
11
+ date: 2021-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dotenv
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.18.1
33
+ version: 0.20.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.18.1
40
+ version: 0.20.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: thor
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.0.1
47
+ version: 1.1.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.0.1
54
+ version: 1.1.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -72,28 +72,28 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 1.10.10
75
+ version: 1.12.5
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 1.10.10
82
+ version: 1.12.5
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: webmock
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 3.10.0
89
+ version: 3.14.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 3.10.0
96
+ version: 3.14.0
97
97
  description: Tools for creating a new project for your Advent of Code solutions, built
98
98
  using Ruby.
99
99
  email:
@@ -103,12 +103,11 @@ executables:
103
103
  extensions: []
104
104
  extra_rdoc_files: []
105
105
  files:
106
+ - ".github/workflows/tests.yml"
106
107
  - ".gitignore"
107
108
  - ".rspec"
108
- - ".travis.yml"
109
109
  - CODE_OF_CONDUCT.md
110
110
  - Gemfile
111
- - Gemfile.lock
112
111
  - LICENSE
113
112
  - LICENSE.txt
114
113
  - README.md
@@ -131,6 +130,7 @@ files:
131
130
  - templates/Gemfile
132
131
  - templates/bin/aoc
133
132
  - templates/solution.rb.erb
133
+ - templates/solution_base.rb
134
134
  - templates/solution_spec.rb.erb
135
135
  - templates/spec/spec_helper.rb
136
136
  homepage: https://github.com/pacso/aoc_rb
@@ -155,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
155
  - !ruby/object:Gem::Version
156
156
  version: '0'
157
157
  requirements: []
158
- rubygems_version: 3.1.2
158
+ rubygems_version: 3.2.3
159
159
  signing_key:
160
160
  specification_version: 4
161
161
  summary: A Ruby toolkit for Advent of Code
data/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- ---
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.7.0
6
- before_install: gem install bundler -v 2.1.2
data/Gemfile.lock DELETED
@@ -1,62 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- aoc_rb (0.0.0)
5
- dotenv (~> 2.7.6)
6
- httparty (~> 0.18.1)
7
- nokogiri (~> 1.10.10)
8
- rspec (~> 3.0)
9
- thor (~> 1.0.1)
10
-
11
- GEM
12
- remote: https://rubygems.org/
13
- specs:
14
- addressable (2.7.0)
15
- public_suffix (>= 2.0.2, < 5.0)
16
- byebug (11.1.3)
17
- crack (0.4.4)
18
- diff-lcs (1.4.4)
19
- dotenv (2.7.6)
20
- hashdiff (1.0.1)
21
- httparty (0.18.1)
22
- mime-types (~> 3.0)
23
- multi_xml (>= 0.5.2)
24
- mime-types (3.3.1)
25
- mime-types-data (~> 3.2015)
26
- mime-types-data (3.2020.1104)
27
- mini_portile2 (2.4.0)
28
- multi_xml (0.6.0)
29
- nokogiri (1.10.10)
30
- mini_portile2 (~> 2.4.0)
31
- public_suffix (4.0.6)
32
- rake (12.3.3)
33
- rspec (3.10.0)
34
- rspec-core (~> 3.10.0)
35
- rspec-expectations (~> 3.10.0)
36
- rspec-mocks (~> 3.10.0)
37
- rspec-core (3.10.0)
38
- rspec-support (~> 3.10.0)
39
- rspec-expectations (3.10.0)
40
- diff-lcs (>= 1.2.0, < 2.0)
41
- rspec-support (~> 3.10.0)
42
- rspec-mocks (3.10.0)
43
- diff-lcs (>= 1.2.0, < 2.0)
44
- rspec-support (~> 3.10.0)
45
- rspec-support (3.10.0)
46
- thor (1.0.1)
47
- webmock (3.10.0)
48
- addressable (>= 2.3.6)
49
- crack (>= 0.3.2)
50
- hashdiff (>= 0.4.0, < 2.0.0)
51
-
52
- PLATFORMS
53
- ruby
54
-
55
- DEPENDENCIES
56
- aoc_rb!
57
- byebug
58
- rake (~> 12.0)
59
- webmock (~> 3.10.0)
60
-
61
- BUNDLED WITH
62
- 2.1.4