From e756f7de40a1b8c6b4a9ebd4ff6eb92b99c68a68 Mon Sep 17 00:00:00 2001 From: Dominic Letz Date: Mon, 12 Apr 2021 10:15:40 +0200 Subject: [PATCH 1/3] Added mix task --- README.md | 11 ++++++++--- lib/mix/duplex.ex | 7 +++++++ 2 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 lib/mix/duplex.ex diff --git a/README.md b/README.md index 315cd4b..db5c7c7 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,14 @@ mix deps.get ## Usage as dependency -```elixir -iex -S mix -Duplex.show_similar +``` +mix duplex +``` + +or with arguments + +``` +mix duplex --threshold 7 ``` ## Config diff --git a/lib/mix/duplex.ex b/lib/mix/duplex.ex new file mode 100644 index 0000000..1843296 --- /dev/null +++ b/lib/mix/duplex.ex @@ -0,0 +1,7 @@ +defmodule Mix.Tasks.Duplex do + use Mix.Task + + def run(args) do + Duplex.main(args) + end +end From a63df9dd2888cabce2cbf67a04a222620142ab3f Mon Sep 17 00:00:00 2001 From: Dominic Letz Date: Mon, 12 Apr 2021 10:19:38 +0200 Subject: [PATCH 2/3] Fixed warnings --- lib/duplex.ex | 35 +++++++++++++---------------------- mix.exs | 2 +- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/lib/duplex.ex b/lib/duplex.ex index 5740113..dfdc4db 100644 --- a/lib/duplex.ex +++ b/lib/duplex.ex @@ -19,32 +19,23 @@ defmodule Duplex do end def parse_args(args) do - {args, _, _} = OptionParser.parse(args) - args = args |> Enum.into(%{}) - extract = fn args, key, to_int -> - if key in Map.keys(args) do - if to_int do - {parsed, _} = Integer.parse(args[key]) - parsed - else - args[key] - end - else - nil - end - end - help = extract.(args, :help, false) - threshold = extract.(args, :threshold, true) - n_jobs = extract.(args, :njobs, true) - export_file = extract.(args, :file, false) + {args, _, _} = + OptionParser.parse(args, + strict: [help: :boolean, threshold: :integer, njobs: :integer, file: :string] + ) + + help = args[:help] + threshold = args[:threshold] + n_jobs = args[:njobs] + export_file = args[:file] {help, threshold, n_jobs, export_file} end def main(args \\ []) do {help, threshold, n_jobs, export_file} = parse_args(args) if help do - IO.puts help_text - help_text + IO.puts(help_text()) + help_text() else Duplex.show_similar(nil, threshold, n_jobs, export_file) end @@ -169,7 +160,7 @@ defmodule Duplex do end) end nodes = for task <- tasks do - Task.await(task, timeout) + Task.await(task, timeout()) end nodes = nodes |> flatten nodes @@ -300,7 +291,7 @@ defmodule Duplex do def hash_map(nodes, map \\ %{}) do if length(nodes) > 0 do - {{n, file}, s} = nodes |> hd + {{_n, file}, s} = nodes |> hd hash = hash_shape(s) map = if hash in Map.keys(map) do Map.put(map, hash, map[hash] ++ [{file, s[:lines], s[:depth]}]) diff --git a/mix.exs b/mix.exs index 948baab..854f5b1 100644 --- a/mix.exs +++ b/mix.exs @@ -15,7 +15,7 @@ defmodule Duplex.Mixfile do end def application do - [applications: [:logger]] + [applications: [:logger], extra_applications: [:dir_walker]] end def escript do From e1629efb73908480ebd3281236d387a3b9d55bbe Mon Sep 17 00:00:00 2001 From: Dominic Letz Date: Mon, 12 Apr 2021 10:21:34 +0200 Subject: [PATCH 3/3] Added `mix format` --- .formatter.exs | 3 + lib/duplex.ex | 363 ++++++++++++++++---------- mix.exs | 31 +-- test/duplex_test.exs | 97 ++++--- test/files_for_tests/example_code1.ex | 15 +- test/files_for_tests/example_code2.ex | 17 +- test/files_for_tests/example_code4.ex | 26 +- 7 files changed, 326 insertions(+), 226 deletions(-) create mode 100644 .formatter.exs diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d304ff3 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,3 @@ +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/lib/duplex.ex b/lib/duplex.ex index dfdc4db..1ac4082 100644 --- a/lib/duplex.ex +++ b/lib/duplex.ex @@ -1,5 +1,4 @@ defmodule Duplex do - @moduledoc """ Duplex allows you to search for similar code blocks inside your Elixir project. @@ -33,6 +32,7 @@ defmodule Duplex do def main(args \\ []) do {help, threshold, n_jobs, export_file} = parse_args(args) + if help do IO.puts(help_text()) help_text() @@ -42,16 +42,19 @@ defmodule Duplex do end defp flatten(e) do - Enum.flat_map(e, &(&1)) + Enum.flat_map(e, & &1) end # Recursively scan directory to find elixir source files def get_files(dir) do if File.exists?(dir) do {:ok, walker} = DirWalker.start_link(dir) - walker |> DirWalker.next(100_000_000) |> Enum.filter(fn item -> - ext = item |> String.split(".") |> Enum.reverse |> hd - (ext == "ex") or (ext == "exs") + + walker + |> DirWalker.next(100_000_000) + |> Enum.filter(fn item -> + ext = item |> String.split(".") |> Enum.reverse() |> hd + ext == "ex" or ext == "exs" end) else [] @@ -71,11 +74,14 @@ defmodule Duplex do defp visit(tnode, nodes) do nodes = nodes ++ [tnode] ch = children(tnode) + if ch do - new_nodes = for c <- ch do - {_, nodes} = visit(c, nodes) - nodes - end + new_nodes = + for c <- ch do + {_, nodes} = visit(c, nodes) + nodes + end + nodes = new_nodes |> flatten {ch, nodes} else @@ -84,18 +90,24 @@ defmodule Duplex do end defp filter_nodes(nodes) do - nodes |> Enum.uniq |> Enum.filter(fn item -> + nodes + |> Enum.uniq() + |> Enum.filter(fn item -> case item do {_, _, nil} -> false + {_, _, c} when is_list(c) -> true + {_, _, _} -> false + nil -> false + _ -> - !(Keyword.keyword?(item)) and is_tuple(item) + !Keyword.keyword?(item) and is_tuple(item) end end) end @@ -109,12 +121,12 @@ defmodule Duplex do # filter short blocks, non deep blocks nodes = nodes |> Enum.filter(fn x -> valid_line_numbers?(x) end) nodes = nodes |> Enum.filter(fn x -> deep_long?(x, threshold) end) - nodes = nodes |> Enum.reverse |> Enum.uniq_by(fn {_, s} -> s[:lines] end) - nodes |> Enum.reverse + nodes = nodes |> Enum.reverse() |> Enum.uniq_by(fn {_, s} -> s[:lines] end) + nodes |> Enum.reverse() end defp valid_line_numbers?({_, %{lines: {min, max}, depth: _}}) do - (max != nil) and (min != nil) + max != nil and min != nil end defp deep_long?({_, %{lines: {min, max}, depth: depth}}, threshold) do @@ -125,7 +137,7 @@ defmodule Duplex do defp read_content(map, files) do if length(files) > 0 do file = hd(files) - map = map |> Map.put(file, file |> File.read! |> String.split("\n")) + map = map |> Map.put(file, file |> File.read!() |> String.split("\n")) read_content(map, tl(files)) else map @@ -139,9 +151,11 @@ defmodule Duplex do c_dirs = Application.get_env(:duplex, :dirs) c_threshold = Application.get_env(:duplex, :threshold) c_n_jobs = Application.get_env(:duplex, :n_jobs) + choose_one = fn i, j, k -> - if i, do: i, else: if j, do: j, else: k + if i, do: i, else: if(j, do: j, else: k) end + dirs = choose_one.(dirs, c_dirs, d_dirs) threshold = choose_one.(threshold, c_threshold, d_threshold) n_jobs = choose_one.(n_jobs, c_n_jobs, d_n_jobs) @@ -150,18 +164,24 @@ defmodule Duplex do end defp read_by_chunk(chunks, threshold) do - tasks = for files <- chunks do - Task.async(fn -> - nodes = for file <- files do - code_blocks(file, threshold) - end - nodes = nodes |> flatten - nodes - end) - end - nodes = for task <- tasks do - Task.await(task, timeout()) - end + tasks = + for files <- chunks do + Task.async(fn -> + nodes = + for file <- files do + code_blocks(file, threshold) + end + + nodes = nodes |> flatten + nodes + end) + end + + nodes = + for task <- tasks do + Task.await(task, timeout()) + end + nodes = nodes |> flatten nodes end @@ -170,16 +190,21 @@ defmodule Duplex do n_jobs = if n_jobs <= 0, do: 1, else: n_jobs n_jobs = if length(files) < n_jobs, do: length(files), else: n_jobs size = div(length(files), n_jobs) - chunks = for n <- 0..(n_jobs - 1) do - cond do - n == 0 -> - Enum.slice(files, 0, size) - n == n_jobs - 1 -> - Enum.slice(files, n * size, 2 * size) - true -> - Enum.slice(files, n * size, size) + + chunks = + for n <- 0..(n_jobs - 1) do + cond do + n == 0 -> + Enum.slice(files, 0, size) + + n == n_jobs - 1 -> + Enum.slice(files, n * size, 2 * size) + + true -> + Enum.slice(files, n * size, size) end - end + end + read_by_chunk(chunks, threshold) end @@ -191,13 +216,17 @@ defmodule Duplex do {:halt, acc} end end + first = current |> hd |> to_charlist |> Enum.reduce_while(0, f) - last = current |> Enum.reverse |> hd |> to_charlist + last = current |> Enum.reverse() |> hd |> to_charlist last = last |> Enum.reduce_while(0, f) + if first != last do - item = content |> Enum.slice(max + 1..max + 1) |> hd + item = content |> Enum.slice((max + 1)..(max + 1)) |> hd + if String.trim(item) == "end" do spaces = item |> to_charlist |> Enum.reduce_while(0, f) + if spaces >= first do get_additional_lines(current ++ [item], min, max + 1, content) else @@ -214,23 +243,30 @@ defmodule Duplex do defp get_output_data(groups, content) do space = ["---------------------------------------------------------------"] # get data to show - all_data = for group <- groups do - gr = for item <- group |> Enum.reverse do - {file, {min, max}, _} = item - min = min - 1 - max = max - 1 - c = Enum.slice(content[file], min..max) - {c, min, max} = get_additional_lines(c, min, max, content[file]) - c = Enum.zip(min + 1..max + 1, c) - c = for l <- c do - {number, line} = l - "#{number}: #{line}" - end - ["#{file}:"] ++ c ++ [""] + all_data = + for group <- groups do + gr = + for item <- group |> Enum.reverse() do + {file, {min, max}, _} = item + min = min - 1 + max = max - 1 + c = Enum.slice(content[file], min..max) + {c, min, max} = get_additional_lines(c, min, max, content[file]) + c = Enum.zip((min + 1)..(max + 1), c) + + c = + for l <- c do + {number, line} = l + "#{number}: #{line}" + end + + ["#{file}:"] ++ c ++ [""] + end + + gr = gr |> flatten + gr ++ space end - gr = gr |> flatten - gr ++ space - end + all_data |> flatten end @@ -241,43 +277,52 @@ defmodule Duplex do configs = get_configs(dirs, t_hold, n_jobs) {dirs, t_hold, n_jobs} = configs # scan dirs - files = for d <- dirs do - get_files(d) - end + files = + for d <- dirs do + get_files(d) + end + files = files |> flatten - IO.puts "Reading files..." + IO.puts("Reading files...") nodes = read_files(files, n_jobs, t_hold) # get map of file contents (key, balue = filename, content) content = read_content(Map.new(), files) - IO.puts "Searching for duplicates..." + IO.puts("Searching for duplicates...") # get grouped equal code blocks groups = nodes |> equal_code all_data = groups |> get_output_data(content) nothing_found = "There are no duplicates" + if export == nil do for item <- all_data do - IO.puts item + IO.puts(item) end + if length(all_data) == 0 do - IO.puts nothing_found + IO.puts(nothing_found) end else - all_data = if length(all_data) == 0 do - [nothing_found] - else - all_data - end + all_data = + if length(all_data) == 0 do + [nothing_found] + else + all_data + end + write_file(export, all_data) end + groups end def write_file(filename, data) do try do {:ok, file} = File.open(filename, [:write]) + for d <- data do IO.binwrite(file, "#{d}\n") end + File.close(file) rescue _ -> @@ -293,11 +338,14 @@ defmodule Duplex do if length(nodes) > 0 do {{_n, file}, s} = nodes |> hd hash = hash_shape(s) - map = if hash in Map.keys(map) do - Map.put(map, hash, map[hash] ++ [{file, s[:lines], s[:depth]}]) - else - Map.put(map, hash, [{file, s[:lines], s[:depth]}]) - end + + map = + if hash in Map.keys(map) do + Map.put(map, hash, map[hash] ++ [{file, s[:lines], s[:depth]}]) + else + Map.put(map, hash, [{file, s[:lines], s[:depth]}]) + end + hash_map(nodes |> tl, map) else map @@ -307,10 +355,13 @@ defmodule Duplex do def hash_shape(shape, hash \\ "") do tmp = if shape[:variable], do: "var", else: inspect(shape[:name]) current = "#{hash}#{tmp}#{length(shape[:children])}" + if length(shape[:children]) > 0 do - ch_hashes = for ch <- shape[:children] do - hash_shape(ch) - end + ch_hashes = + for ch <- shape[:children] do + hash_shape(ch) + end + current <> (ch_hashes |> Enum.join("")) else current @@ -321,32 +372,46 @@ defmodule Duplex do def equal_code(nodes) do groups = nodes |> hash_map # keep only groups with size > 1 - hashes = groups |> Map.keys - single_nodes = hashes |> Enum.filter(fn hash -> - length(groups[hash]) == 1 - end) + hashes = groups |> Map.keys() + + single_nodes = + hashes + |> Enum.filter(fn hash -> + length(groups[hash]) == 1 + end) + groups = Map.drop(groups, single_nodes) # filter subsamples keys = Map.keys(groups) - not_subsamples = Enum.filter(keys, fn hash -> - not subsample?(hash, keys) - end) - groups = for hash <- not_subsamples, do: groups[hash] - groups = for gr <- groups do - gr |> Enum.sort_by(fn {file, {min, _}, _} -> - {file, -min} + + not_subsamples = + Enum.filter(keys, fn hash -> + not subsample?(hash, keys) end) - end - groups |> Enum.sort_by(fn gr -> + + groups = for hash <- not_subsamples, do: groups[hash] + + groups = + for gr <- groups do + gr + |> Enum.sort_by(fn {file, {min, _}, _} -> + {file, -min} + end) + end + + groups + |> Enum.sort_by(fn gr -> {_, {min, max}, depth} = gr |> hd - - (max - min + depth) * length(gr) + -(max - min + depth) * length(gr) end) end defp subsample?(hash, keys) do - tmp = for key <- keys do - String.length(hash) < String.length(key) and String.contains?(key, hash) - end + tmp = + for key <- keys do + String.length(hash) < String.length(key) and String.contains?(key, hash) + end + Enum.any?(tmp) end @@ -355,12 +420,15 @@ defmodule Duplex do case tnode do {_, _, nodes} -> nodes + _ -> cond do Keyword.keyword?(tnode) -> - tnode |> Enum.into(%{}) |> Map.values + tnode |> Enum.into(%{}) |> Map.values() + is_list(tnode) -> tnode + true -> nil end @@ -370,80 +438,85 @@ defmodule Duplex do # Get structured shape of the node for comparison def get_shape(tnode) do ch = children(tnode) - ch = if ch do - for c <- ch do - get_shape(c) + + ch = + if ch do + for c <- ch do + get_shape(c) + end + else + [] end - else - [] - end - depth = if length(ch) == 0 do - 1 - else - Enum.max_by(ch, fn item -> item[:depth] end)[:depth] + 1 - end + + depth = + if length(ch) == 0 do + 1 + else + Enum.max_by(ch, fn item -> item[:depth] end)[:depth] + 1 + end + case tnode do {name, _, content} -> # is node a variable? - var = (content == nil) - name = if is_atom(name) do - name - else - "_" - end - %{name: name, - lines: get_lines(tnode), - children: ch, - variable: var, - depth: depth} + var = content == nil + + name = + if is_atom(name) do + name + else + "_" + end + + %{name: name, lines: get_lines(tnode), children: ch, variable: var, depth: depth} + _ -> if is_integer(tnode) or is_float(tnode) do - %{name: tnode, - lines: get_lines(tnode), - children: ch, - variable: false, - depth: depth} + %{name: tnode, lines: get_lines(tnode), children: ch, variable: false, depth: depth} else - %{name: "_", - lines: get_lines(tnode), - children: ch, - variable: false, - depth: depth} + %{name: "_", lines: get_lines(tnode), children: ch, variable: false, depth: depth} end end end # Recursively go through the node and finds {min, max} line numbers defp get_lines(tnode, main \\ true) do - current = case tnode do - {_, data, _} -> - case data do - [line: l] -> - l - [counter: _, line: l] -> - l - _ -> - nil - end - _ -> - nil - end + current = + case tnode do + {_, data, _} -> + case data do + [line: l] -> + l + + [counter: _, line: l] -> + l + + _ -> + nil + end + + _ -> + nil + end + ch = children(tnode) - from_ch = if ch do - for c <- ch do - get_lines(c, false) + + from_ch = + if ch do + for c <- ch do + get_lines(c, false) + end + else + [] end - else - [] - end + from_ch = from_ch |> flatten current = Enum.filter(from_ch, fn item -> item != nil end) ++ [current] current = Enum.uniq(current) + if main do {Enum.min(current), Enum.max(current)} else current end end - end diff --git a/mix.exs b/mix.exs index 854f5b1..15ca39b 100644 --- a/mix.exs +++ b/mix.exs @@ -2,16 +2,18 @@ defmodule Duplex.Mixfile do use Mix.Project def project do - [app: :duplex, - version: "0.1.1", - elixir: "~> 1.3", - build_embedded: Mix.env == :prod, - start_permanent: Mix.env == :prod, - description: description(), - package: package(), - test_coverage: [tool: Coverex.Task], - deps: deps(), - escript: escript()] + [ + app: :duplex, + version: "0.1.1", + elixir: "~> 1.3", + build_embedded: Mix.env() == :prod, + start_permanent: Mix.env() == :prod, + description: description(), + package: package(), + test_coverage: [tool: Coverex.Task], + deps: deps(), + escript: escript() + ] end def application do @@ -53,11 +55,10 @@ defmodule Duplex.Mixfile do defp package do [ - name: :duplex, - maintainers: ["Ivan Cherevko", "Andrew Koryagin"], - licenses: ["Apache 2.0"], - links: %{}, + name: :duplex, + maintainers: ["Ivan Cherevko", "Andrew Koryagin"], + licenses: ["Apache 2.0"], + links: %{} ] end - end diff --git a/test/duplex_test.exs b/test/duplex_test.exs index 758ec2c..9f6fbe4 100644 --- a/test/duplex_test.exs +++ b/test/duplex_test.exs @@ -4,12 +4,15 @@ defmodule DuplexTest do import Duplex test "getting all .ex, .exs files" do - assert Duplex.get_files("test") == ["test/test_helper.exs", - "test/files_for_tests/example_code4.ex", - "test/files_for_tests/example_code3.ex", - "test/files_for_tests/example_code2.ex", - "test/files_for_tests/example_code1.ex", - "test/duplex_test.exs"] + assert Duplex.get_files("test") == [ + "test/test_helper.exs", + "test/files_for_tests/example_code4.ex", + "test/files_for_tests/example_code3.ex", + "test/files_for_tests/example_code2.ex", + "test/files_for_tests/example_code1.ex", + "test/duplex_test.exs" + ] + assert Duplex.get_files("path_does_not_exist") == [] end @@ -17,25 +20,38 @@ defmodule DuplexTest do dir = "test/files_for_tests/" name = "example_code" ext = ".ex" - f_names = ["#{dir}#{name}1#{ext}", - "#{dir}#{name}2#{ext}", - "#{dir}#{name}3#{ext}", - "#{dir}#{name}4#{ext}"] - files = if f_index do - Enum.slice(f_names, f_index..f_index) - else - f_names - end - nodes = for file <- files do - code_blocks(file, threshold) - end - nodes |> Enum.flat_map(&(&1)) + + f_names = [ + "#{dir}#{name}1#{ext}", + "#{dir}#{name}2#{ext}", + "#{dir}#{name}3#{ext}", + "#{dir}#{name}4#{ext}" + ] + + files = + if f_index do + Enum.slice(f_names, f_index..f_index) + else + f_names + end + + nodes = + for file <- files do + code_blocks(file, threshold) + end + + nodes |> Enum.flat_map(& &1) end test "example_code duplicates" do - results = [[{"test/files_for_tests/example_code1.ex", {1, 14}, 13}, - {"test/files_for_tests/example_code2.ex", {1, 14}, 13}, - {"test/files_for_tests/example_code4.ex", {2, 16}, 13}]] + results = [ + [ + {"test/files_for_tests/example_code1.ex", {1, 14}, 13}, + {"test/files_for_tests/example_code2.ex", {1, 14}, 13}, + {"test/files_for_tests/example_code4.ex", {2, 16}, 13} + ] + ] + f_name = "test_res.txt" dir = ["test/files_for_tests"] assert Duplex.show_similar(dir, nil, nil, f_name) == results @@ -56,14 +72,20 @@ defmodule DuplexTest do dir = "test/files_for_tests/" name = "example_code" ext = ".ex" - files = ["#{dir}#{name}1#{ext}", - "#{dir}#{name}2#{ext}", - "#{dir}#{name}3#{ext}", - "#{dir}#{name}4#{ext}"] - chunks = for i <- 1..8 do - Duplex.read_files(files, i, 7) - end - assert chunks |> Enum.uniq |> length == 1 + + files = [ + "#{dir}#{name}1#{ext}", + "#{dir}#{name}2#{ext}", + "#{dir}#{name}3#{ext}", + "#{dir}#{name}4#{ext}" + ] + + chunks = + for i <- 1..8 do + Duplex.read_files(files, i, 7) + end + + assert chunks |> Enum.uniq() |> length == 1 end test "shape hashes" do @@ -73,17 +95,15 @@ defmodule DuplexTest do {_, shape1} = nodes1 |> Enum.max_by(fn {_, shape} -> shape[:depth] end) {_, shape2} = nodes2 |> Enum.max_by(fn {_, shape} -> shape[:depth] end) {_, shape3} = nodes3 |> Enum.max_by(fn {_, shape} -> shape[:depth] end) - h1 = shape1 |> Duplex.hash_shape - h2 = shape2 |> Duplex.hash_shape - h3 = shape3 |> Duplex.hash_shape + h1 = shape1 |> Duplex.hash_shape() + h2 = shape2 |> Duplex.hash_shape() + h3 = shape3 |> Duplex.hash_shape() assert h1 == h2 assert h1 != h3 end test "argparse" do - args = ["--help", "--njobs", "10", - "--threshold", "2", "--file", - "/path/to/file.ex"] + args = ["--help", "--njobs", "10", "--threshold", "2", "--file", "/path/to/file.ex"] assert Duplex.parse_args(args) == {true, 2, 10, "/path/to/file.ex"} end @@ -92,8 +112,8 @@ defmodule DuplexTest do end test "escript" do - assert Duplex.main(["--help"]) == Duplex.help_text - assert Duplex.main == Duplex.show_similar + assert Duplex.main(["--help"]) == Duplex.help_text() + assert Duplex.main() == Duplex.show_similar() end test "writting file" do @@ -102,5 +122,4 @@ defmodule DuplexTest do assert File.read!(f_name) == "data1\ndata2\n" assert File.rm(f_name) == :ok end - end diff --git a/test/files_for_tests/example_code1.ex b/test/files_for_tests/example_code1.ex index 9bd5d4b..d3e619d 100644 --- a/test/files_for_tests/example_code1.ex +++ b/test/files_for_tests/example_code1.ex @@ -4,14 +4,15 @@ defmodule M1 do """ def f(a, b) do if b != 0 do - if a > b do - a = a - b - else - b = b - a - end - f(a, b) + if a > b do + a = a - b + else + b = b - a + end + + f(a, b) else - a + a end end end diff --git a/test/files_for_tests/example_code2.ex b/test/files_for_tests/example_code2.ex index aee244e..b41db84 100644 --- a/test/files_for_tests/example_code2.ex +++ b/test/files_for_tests/example_code2.ex @@ -1,17 +1,18 @@ - defmodule M2 do +defmodule M2 do @moduledoc """ M2 """ def f(n, m) do if m != 0 do - if n > m do - n = n - m - else - m = m - n - end - f(n, m) + if n > m do + n = n - m + else + m = m - n + end + + f(n, m) else - n + n end end end diff --git a/test/files_for_tests/example_code4.ex b/test/files_for_tests/example_code4.ex index ee0dc42..6dc9f2f 100644 --- a/test/files_for_tests/example_code4.ex +++ b/test/files_for_tests/example_code4.ex @@ -1,22 +1,24 @@ - defmodule M4 do @moduledoc """ M4 """ def f(a, b) do if b != 0 do - if a > b do - a = a - b - else - b = b - a - # 1 - end - f(a, b) + if a > b do + a = a - b + else + b = b - a + # 1 + end + + f(a, b) else - a - # 2 + a + # 2 end - # 3 + + # 3 end -# 4 + + # 4 end