aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--challenge-001/paulo-custodio/test.pl4
-rw-r--r--challenge-024/paulo-custodio/c/ch-2.c294
-rw-r--r--challenge-024/paulo-custodio/perl/ch-2.pl3
-rw-r--r--challenge-024/paulo-custodio/python/ch-2.py5
-rw-r--r--challenge-024/paulo-custodio/t/test-2.yaml28
-rw-r--r--challenge-214/paulo-custodio/Makefile2
-rw-r--r--challenge-214/paulo-custodio/perl/ch-1.pl95
-rw-r--r--challenge-214/paulo-custodio/perl/ch-2.pl74
-rw-r--r--challenge-214/paulo-custodio/t/test-1.yaml20
-rw-r--r--challenge-214/paulo-custodio/t/test-2.yaml20
-rw-r--r--challenge-215/paulo-custodio/Makefile2
-rw-r--r--challenge-215/paulo-custodio/perl/ch-1.pl47
-rw-r--r--challenge-215/paulo-custodio/perl/ch-2.pl53
-rw-r--r--challenge-215/paulo-custodio/t/test-1.yaml15
-rw-r--r--challenge-215/paulo-custodio/t/test-2.yaml15
15 files changed, 659 insertions, 18 deletions
diff --git a/challenge-001/paulo-custodio/test.pl b/challenge-001/paulo-custodio/test.pl
index e05049e9fd..132d8f52be 100644
--- a/challenge-001/paulo-custodio/test.pl
+++ b/challenge-001/paulo-custodio/test.pl
@@ -148,11 +148,11 @@ sub build {
return "python3 ../../challenge-001/paulo-custodio/brainfuck.py $prog_wo_ext.bf";
}
if (/^c$/) {
- run("gcc $prog -o $prog_wo_ext -lm -lmpfr -lgmp") if (!-f $exe || -M $exe > -M $prog);
+ run("gcc $prog -o $prog_wo_ext -lm -lmpfr -lgmp -lsqlite3") if (!-f $exe || -M $exe > -M $prog);
return $exe;
}
if (/^cpp$/) {
- run("g++ $prog -o $prog_wo_ext -lmpfr -lgmpxx -lgmp") if (!-f $exe || -M $exe > -M $prog);
+ run("g++ $prog -o $prog_wo_ext -lmpfr -lgmpxx -lgmp -lsqlite3") if (!-f $exe || -M $exe > -M $prog);
return $exe;
}
if (/^d$/) {
diff --git a/challenge-024/paulo-custodio/c/ch-2.c b/challenge-024/paulo-custodio/c/ch-2.c
new file mode 100644
index 0000000000..e3424eec6b
--- /dev/null
+++ b/challenge-024/paulo-custodio/c/ch-2.c
@@ -0,0 +1,294 @@
+/*
+Challenge 024
+
+Task #2
+Create a script to implement full text search functionality using Inverted
+Index. According to wikipedia:
+
+In computer science, an inverted index (also referred to as a postings file
+or inverted file) is a database index storing a mapping from content, such as
+words or numbers, to its locations in a table, or in a document or a set of
+documents (named in contrast to a forward index, which maps from documents to
+content). The purpose of an inverted index is to allow fast full-text
+searches, at a cost of increased processing when a document is added to the
+database.
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sqlite3.h>
+
+#define DATABASE "index.db3"
+#define SEPARATORS " \t\r\n\v\f!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~"
+
+void* check_mem(void* p) {
+ if (!p) {
+ fputs("Out of memory\n", stderr);
+ exit(EXIT_FAILURE);
+ }
+ return p;
+}
+
+bool file_exists(const char *filename) {
+ struct stat buffer;
+ return (stat(filename, &buffer) == 0);
+}
+
+void basename(char* filename) {
+ char* p = filename + strlen(filename);
+ while (p > filename && p[-1] != '/' && p[-1] != '\\' && p[-1] != '.')
+ p--;
+ if (p > filename && p[-1] == '.')
+ p[-1] = '\0';
+}
+
+char* strtolower(char* str) {
+ for (char* p = str; *p; p++)
+ *p = tolower(*p);
+ return str;
+}
+
+void check_error(sqlite3* db, int rc) {
+ if (rc != SQLITE_OK) {
+ fprintf(stderr, "Database error: %s\n", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ exit(EXIT_FAILURE);
+ }
+}
+
+void check_error_step(sqlite3* db, int step) {
+ if (step != SQLITE_DONE) {
+ fprintf(stderr, "Database error: %s\n", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ exit(EXIT_FAILURE);
+ }
+}
+
+int callback(void *NotUsed, int argc, char **argv, char **azColName) {
+ for(int i = 0; i < argc; i++) {
+ printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
+ }
+ printf("\n");
+ return 0;
+}
+
+sqlite3* open_database() {
+ sqlite3 *db;
+ int rc = sqlite3_open(DATABASE, &db);
+ check_error(db, rc);
+ return db;
+}
+
+void close_database(sqlite3* db) {
+ sqlite3_close(db);
+}
+
+void create_database() {
+ if (file_exists(DATABASE))
+ return;
+
+ sqlite3 *db = open_database();
+ char *zErrMsg = NULL;
+
+ const char* sql =
+ "CREATE TABLE words ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "word TEXT UNIQUE);"
+ "CREATE TABLE documents ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "title TEXT UNIQUE); "
+ "CREATE TABLE found ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "document_id INTEGER, "
+ "word_id INTEGER); "
+ "CREATE UNIQUE INDEX found_index ON found(document_id, word_id); ";
+
+ int rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
+ if (rc != SQLITE_OK) {
+ fprintf(stderr, "SQL error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ sqlite3_close(db);
+ return;
+ }
+
+ close_database(db);
+}
+
+int get_or_add_document_id(sqlite3* db, const char* title) {
+ bool found = false;
+ int id = -1;
+
+ const char* sql = "SELECT id FROM documents WHERE title = ?;";
+ sqlite3_stmt* stmt;
+ int rc = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
+ check_error(db, rc);
+ sqlite3_bind_text(stmt, 1, title, strlen(title), NULL);
+ int step = sqlite3_step(stmt);
+ if (step == SQLITE_ROW) {
+ id = sqlite3_column_int(stmt, 0);
+ found = true;
+ }
+ sqlite3_finalize(stmt);
+
+ if (found)
+ return id;
+
+ sql = "INSERT INTO documents(title) VALUES(?);";
+ rc = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
+ check_error(db, rc);
+ sqlite3_bind_text(stmt, 1, title, strlen(title), NULL);
+ step = sqlite3_step(stmt);
+ check_error_step(db, step);
+ sqlite3_finalize(stmt);
+
+ return sqlite3_last_insert_rowid(db);
+}
+
+int get_or_add_word_id(sqlite3* db, const char* word) {
+ bool found = false;
+ int id = -1;
+
+ const char* sql = "SELECT id FROM words WHERE word = ?;";
+ sqlite3_stmt* stmt;
+ int rc = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
+ check_error(db, rc);
+ sqlite3_bind_text(stmt, 1, word, strlen(word), NULL);
+ int step = sqlite3_step(stmt);
+ if (step == SQLITE_ROW) {
+ id = sqlite3_column_int(stmt, 0);
+ found = true;
+ }
+ sqlite3_finalize(stmt);
+
+ if (found)
+ return id;
+
+ sql = "INSERT INTO words(word) VALUES(?);";
+ rc = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
+ check_error(db, rc);
+ sqlite3_bind_text(stmt, 1, word, strlen(word), NULL);
+ step = sqlite3_step(stmt);
+ check_error_step(db, step);
+ sqlite3_finalize(stmt);
+
+ return sqlite3_last_insert_rowid(db);
+}
+
+int add_found(sqlite3* db, int document_id, int word_id) {
+ bool found = false;
+ int id = -1;
+
+ const char* sql = "SELECT id FROM found "
+ "WHERE document_id = ? AND word_id = ?;";
+ sqlite3_stmt* stmt;
+ int rc = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
+ check_error(db, rc);
+ sqlite3_bind_int(stmt, 1, document_id);
+ sqlite3_bind_int(stmt, 2, word_id);
+ int step = sqlite3_step(stmt);
+ if (step == SQLITE_ROW) {
+ id = sqlite3_column_int(stmt, 0);
+ found = true;
+ }
+ sqlite3_finalize(stmt);
+
+ if (found)
+ return id;
+
+ sql = "INSERT INTO found(document_id, word_id) VALUES(?, ?);";
+ rc = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
+ check_error(db, rc);
+ sqlite3_bind_int(stmt, 1, document_id);
+ sqlite3_bind_int(stmt, 2, word_id);
+ step = sqlite3_step(stmt);
+ check_error_step(db, step);
+ sqlite3_finalize(stmt);
+
+ return sqlite3_last_insert_rowid(db);
+}
+
+void add_document(const char* filename) {
+ sqlite3* db = open_database();
+
+ // title
+ char* title = check_mem(strdup(filename));
+ basename(title);
+
+ // document id
+ int document_id = get_or_add_document_id(db, title);
+
+ // read document
+ FILE* fp = fopen(filename, "r");
+ if (fp == NULL) {
+ perror(filename);
+ close_database(db);
+ free(title);
+ exit(EXIT_FAILURE);
+ }
+
+ char line[BUFSIZ];
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ char* word = strtok(line, SEPARATORS);
+ while (word != NULL) {
+ strtolower(word);
+ int word_id = get_or_add_word_id(db, word);
+ add_found(db, document_id, word_id);
+ word = strtok(NULL, SEPARATORS);
+ }
+ }
+
+ printf("Indexed %s\n", title);
+
+ fclose(fp);
+ close_database(db);
+ free(title);
+}
+
+void search_documents(const char* word) {
+ sqlite3* db = open_database();
+
+ char* lcword = strtolower(check_mem(strdup(word)));
+
+ const char* sql = "SELECT word, title "
+ "FROM documents, words, found "
+ "WHERE word = ? "
+ "AND found.document_id = documents.id "
+ "AND found.word_id = words.id "
+ "ORDER BY title;";
+ sqlite3_stmt* stmt;
+ int rc = sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, NULL);
+ check_error(db, rc);
+ sqlite3_bind_text(stmt, 1, lcword, strlen(lcword), NULL);
+ int step;
+ while ((step = sqlite3_step(stmt)) == SQLITE_ROW) {
+ printf("%s %s\n",
+ sqlite3_column_text(stmt, 0),
+ sqlite3_column_text(stmt, 1));
+ }
+ sqlite3_finalize(stmt);
+
+ close_database(db);
+ free(lcword);
+}
+
+int usage(void) {
+ fputs("usage: ch-2 add|search file|word\n", stderr);
+ return EXIT_FAILURE;
+}
+
+int main(int argc, char* argv[]) {
+ if (argc != 3)
+ return usage();
+
+ create_database();
+ if (strcmp(argv[1], "add") == 0)
+ add_document(argv[2]);
+ else if (strcmp(argv[1], "search") == 0)
+ search_documents(argv[2]);
+ else
+ return usage();
+}
diff --git a/challenge-024/paulo-custodio/perl/ch-2.pl b/challenge-024/paulo-custodio/perl/ch-2.pl
index 2d74437c97..bd938b5a74 100644
--- a/challenge-024/paulo-custodio/perl/ch-2.pl
+++ b/challenge-024/paulo-custodio/perl/ch-2.pl
@@ -41,6 +41,7 @@ CREATE TABLE found (
document_id INTEGER,
word_id INTEGER
);
+CREATE UNIQUE INDEX found_index ON found(document_id, word_id);
END
close($p) or die "sqlite3 failed";
}
@@ -64,7 +65,7 @@ sub add_doc {
my($doc) = @_;
# get title
- my $title = path($doc)->basename;
+ (my $title = path($doc)->basename) =~ s/\.\w+$//;
# connect to index database
my $dbh = DBI->connect("dbi:SQLite:dbname=".DBFILE,"","",
diff --git a/challenge-024/paulo-custodio/python/ch-2.py b/challenge-024/paulo-custodio/python/ch-2.py
index 7b3aff407a..6f4ab47bb9 100644
--- a/challenge-024/paulo-custodio/python/ch-2.py
+++ b/challenge-024/paulo-custodio/python/ch-2.py
@@ -47,6 +47,9 @@ def create_database():
word_id INTEGER
);
''')
+ cur.execute('''
+ CREATE UNIQUE INDEX found_index ON found(document_id, word_id);
+ ''')
con.commit()
con.close()
@@ -94,7 +97,7 @@ def add_found(con, document_id, word_id):
def add_doc(file):
con = sqlite3.connect(DBFILE)
- title = os.path.basename(file)
+ title = re.sub(r"\.\w+$", "", os.path.basename(file))
document_id = get_document_id(con, title)
# read document
diff --git a/challenge-024/paulo-custodio/t/test-2.yaml b/challenge-024/paulo-custodio/t/test-2.yaml
index 1f15630cb9..7df1a09659 100644
--- a/challenge-024/paulo-custodio/t/test-2.yaml
+++ b/challenge-024/paulo-custodio/t/test-2.yaml
@@ -1,52 +1,52 @@
-- setup:
+- setup: unlink('index.db3');1
cleanup:
args: add 'The Cask of Amontillado.txt'
input:
output: |
- Indexed The Cask of Amontillado.txt
+ Indexed The Cask of Amontillado
- setup:
cleanup:
args: add 'The Fall of the House of Usher.txt'
input:
output: |
- Indexed The Fall of the House of Usher.txt
+ Indexed The Fall of the House of Usher
- setup:
cleanup:
args: add 'The Masque of the Red Death.txt'
input:
output: |
- Indexed The Masque of the Red Death.txt
+ Indexed The Masque of the Red Death
- setup:
cleanup:
args: add 'The Raven.txt'
input:
output: |
- Indexed The Raven.txt
+ Indexed The Raven
- setup:
cleanup:
args: search death
input:
output: |
- death The Fall of the House of Usher.txt
- death The Masque of the Red Death.txt
- death The Raven.txt
+ death The Fall of the House of Usher
+ death The Masque of the Red Death
+ death The Raven
- setup:
cleanup:
args: search mystery
input:
output: |
- mystery The Fall of the House of Usher.txt
- mystery The Raven.txt
+ mystery The Fall of the House of Usher
+ mystery The Raven
- setup:
cleanup:
args: search revenge
input:
output: |
- revenge The Cask of Amontillado.txt
+ revenge The Cask of Amontillado
- setup:
- cleanup: unlink('index.db3')
+ cleanup: unlink('index.db3');1
args: search imagination
input:
output: |
- imagination The Fall of the House of Usher.txt
- imagination The Raven.txt
+ imagination The Fall of the House of Usher
+ imagination The Raven
diff --git a/challenge-214/paulo-custodio/Makefile b/challenge-214/paulo-custodio/Makefile
new file mode 100644
index 0000000000..c3c762d746
--- /dev/null
+++ b/challenge-214/paulo-custodio/Makefile
@@ -0,0 +1,2 @@
+all:
+ perl ../../challenge-001/paulo-custodio/test.pl
diff --git a/challenge-214/paulo-custodio/perl/ch-1.pl b/challenge-214/paulo-custodio/perl/ch-1.pl
new file mode 100644
index 0000000000..0296e952f1
--- /dev/null
+++ b/challenge-214/paulo-custodio/perl/ch-1.pl
@@ -0,0 +1,95 @@
+#!/usr/bin/perl
+
+# Challenge 214
+#
+# Task 1: Rank Score
+# Submitted by: Mohammad S Anwar
+#
+# You are given a list of scores (>=1).
+#
+# Write a script to rank each score in descending order. First three will get
+# medals i.e. G (Gold), S (Silver) and B (Bronze). Rest will just get the
+# ranking number.
+#
+# Using the standard model of giving equal scores equal rank, then
+# advancing that number of ranks.
+#
+#
+# Example 1
+#
+# Input: @scores = (1,2,4,3,5)
+# Output: (5,4,S,B,G)
+#
+# Score 1 is the 5th rank.
+# Score 2 is the 4th rank.
+# Score 4 is the 2nd rank i.e. Silver (S).
+# Score 3 is the 3rd rank i.e. Bronze (B).
+# Score 5 is the 1st rank i.e. Gold (G).
+#
+# Example 2
+#
+# Input: @scores = (8,5,6,7,4)
+# Output: (G,4,B,S,5)
+#
+# Score 8 is the 1st rank i.e. Gold (G).
+# Score 4 is the 4th rank.
+# Score 6 is the 3rd rank i.e. Bronze (B).
+# Score 7 is the 2nd rank i.e. Silver (S).
+# Score 4 is the 5th rank.
+#
+# Example 3
+#
+# Input: @list = (3,5,4,2)
+# Output: (B,G,S,4)
+#
+# Example 4
+#
+# Input: @scores = (2,5,2,1,7,5,1)
+# Output: (4,S,4,6,G,S,6)
+
+use Modern::Perl;
+
+# in: list of numbers
+# out: list of items with the same value reverse-ordered by value
+# each item is [index, value]
+sub rank_values {
+ my(@list) = @_;
+ my @sorted = reverse sort {$a->[1] <=> $b->[1]}
+ map {[$_, $list[$_]]} 0..$#list;
+ my @output;
+ while (@sorted) {
+ my @head;
+ my $first_value = $sorted[0][1];
+ while (@sorted && $sorted[0][1] == $first_value) {
+ push @head, shift @sorted;
+ }
+ push @output, \@head;
+ }
+ return @output;
+}
+
+# in: list of numbers
+# out: corresponding list of ranking
+sub standard_ranking {
+ my(@list) = @_;
+ my @ranked = rank_values(@list);
+ my @ranks;
+ my $rank = 1;
+ while (@ranked) {
+ my @head = @{shift @ranked};
+ for (@head) {
+ $ranks[$_->[0]] = $rank;
+ }
+ $rank += @head;
+ }
+
+ for (@ranks) {
+ s/^1$/G/;
+ s/^2$/S/;
+ s/^3$/B/;
+ }
+
+ return @ranks;
+}
+
+say join(" ", standard_ranking(@ARGV));
diff --git a/challenge-214/paulo-custodio/perl/ch-2.pl b/challenge-214/paulo-custodio/perl/ch-2.pl
new file mode 100644
index 0000000000..29c12ad197
--- /dev/null
+++ b/challenge-214/paulo-custodio/perl/ch-2.pl
@@ -0,0 +1,74 @@
+#!/usr/bin/perl
+
+# Challenge 214
+#
+# Task 2: Collect Points
+# Submitted by: Mohammad S Anwar
+#
+# You are given a list of numbers.
+#
+# You will perform a series of removal operations. For each operation, you
+# remove from the list N (one or more) equal and consecutive numbers, and
+# add to your score N × N.
+#
+# Determine the maximum possible score.
+# Example 1:
+#
+# Input: @numbers = (2,4,3,3,3,4,5,4,2)
+# Output: 23
+#
+# We see three 3's next to each other so let us remove that first and
+# collect 3 x 3 points.
+# So now the list is (2,4,4,5,4,2).
+# Let us now remove 5 so that all 4's can be next to each other and
+# collect 1 x 1 point.
+# So now the list is (2,4,4,4,2).
+# Time to remove three 4's and collect 3 x 3 points.
+# Now the list is (2,2).
+# Finally remove both 2's and collect 2 x 2 points.
+# So the total points collected is 9 + 1 + 9 + 4 => 23.
+#
+# Example 2:
+#
+# Input: @numbers = (1,2,2,2,2,1)
+# Output: 20
+#
+# Remove four 2's first and collect 4 x 4 points.
+# Now the list is (1,1).
+# Finally remove the two 1's and collect 2 x 2 points.
+# So the total points collected is 16 + 4 => 20.
+#
+# Example 3:
+#
+# Input: @numbers = (1)
+# Output: 1
+#
+# Example 4:
+#
+# Input: @numbers = (2,2,2,1,1,2,2,2)
+# Output: 40
+#
+# Remove two 1's = 2 x 2 points.
+# Now the list is (2,2,2,2,2,2).
+# Then reomove six 2's = 6 x 6 points.
+
+use Modern::Perl;
+
+sub remove_numbers {
+ my(@in) = @_;
+ return scalar(@in) if @in < 2;
+ my $best_score = 0;
+ for (my $s = 0; $s < @in; $s++) {
+ my $e = $s;
+ while ($e < @in && $in[$s]==$in[$e]) {
+ $e++;
+ }
+ my @new_list = (@in[0..$s-1], @in[$e..$#in]);
+ my $score = ($e-$s)*($e-$s) + remove_numbers(@new_list);
+ $best_score = $score if $best_score < $score;
+ $s = $e-1;
+ }
+ return $best_score;
+}
+
+say remove_numbers(@ARGV);
diff --git a/challenge-214/paulo-custodio/t/test-1.yaml b/challenge-214/paulo-custodio/t/test-1.yaml
new file mode 100644
index 0000000000..a4e13cd4ac
--- /dev/null
+++ b/challenge-214/paulo-custodio/t/test-1.yaml
@@ -0,0 +1,20 @@
+- setup:
+ cleanup:
+ args: 1 2 4 3 5
+ input:
+ output: 5 4 S B G
+- setup:
+ cleanup:
+ args: 8 5 6 7 4
+ input:
+ output: G 4 B S 5
+- setup:
+ cleanup:
+ args: 3 5 4 2
+ input:
+ output: B G S 4
+- setup:
+ cleanup:
+ args: 2 5 2 1 7 5 1
+ input:
+ output: 4 S 4 6 G S 6
diff --git a/challenge-214/paulo-custodio/t/test-2.yaml b/challenge-214/paulo-custodio/t/test-2.yaml
new file mode 100644
index 0000000000..104f6ef58c
--- /dev/null
+++ b/challenge-214/paulo-custodio/t/test-2.yaml
@@ -0,0 +1,20 @@
+- setup:
+ cleanup:
+ args: 2 4 3 3 3 4 5 4 2
+ input:
+ output: 23
+- setup:
+ cleanup:
+ args: 1 2 2 2 2 1
+ input:
+ output: 20
+- setup:
+ cleanup:
+ args: 1
+ input:
+ output: 1
+- setup:
+ cleanup:
+ args: 2 2 2 1 1 2 2 2
+ input:
+ output: 40
diff --git a/challenge-215/paulo-custodio/Makefile b/challenge-215/paulo-custodio/Makefile
new file mode 100644
index 0000000000..c3c762d746
--- /dev/null
+++ b/challenge-215/paulo-custodio/Makefile
@@ -0,0 +1,2 @@
+all:
+ perl ../../challenge-001/paulo-custodio/test.pl
diff --git a/challenge-215/paulo-custodio/perl/ch-1.pl b/challenge-215/paulo-custodio/perl/ch-1.pl
new file mode 100644
index 0000000000..04e13da339
--- /dev/null
+++ b/challenge-215/paulo-custodio/perl/ch-1.pl
@@ -0,0 +1,47 @@
+#!/usr/bin/perl
+
+# Challenge 215
+#
+# Task 1: Odd one Out
+# Submitted by: Mohammad S Anwar
+#
+# You are given a list of words (alphabetic characters only) of same size.
+#
+# Write a script to remove all words not sorted alphabetically and print
+# the number of words in the list that are not alphabetically sorted.
+#
+# Example 1
+#
+# Input: @words = ('abc', 'xyz', 'tsu')
+# Output: 1
+#
+# The words 'abc' and 'xyz' are sorted and can't be removed.
+# The word 'tsu' is not sorted and hence can be removed.
+#
+# Example 2
+#
+# Input: @words = ('rat', 'cab', 'dad')
+# Output: 3
+#
+# None of the words in the given list are sorted.
+# Therefore all three needs to be removed.
+#
+# Example 3
+#
+# Input: @words = ('x', 'y', 'z')
+# Output: 0
+
+use Modern::Perl;
+
+sub is_sorted {
+ my($str) = @_;
+ my $sorted = join '', sort {$a cmp $b} split //, $str;
+ return $str eq $sorted;
+}
+
+sub odd_out {
+ my(@in) = @_;
+ return scalar(grep {!is_sorted($_)} @in);
+}
+
+say odd_out(@ARGV);
diff --git a/challenge-215/paulo-custodio/perl/ch-2.pl b/challenge-215/paulo-custodio/perl/ch-2.pl
new file mode 100644
index 0000000000..9a55f27be1
--- /dev/null
+++ b/challenge-215/paulo-custodio/perl/ch-2.pl
@@ -0,0 +1,53 @@
+#!/usr/bin/perl
+
+# Challenge 215
+#
+# Task 2: Number Placement
+# Submitted by: Mohammad S Anwar
+#
+# You are given a list of numbers having just 0 and 1. You are also given
+# placement count (>=1).
+#
+# Write a script to find out if it is possible to replace 0 with 1 in the
+# given list. The only condition is that you can only replace when there
+# is no 1 on either side. Print 1 if it is possible otherwise 0.
+# Example 1:
+#
+# Input: @numbers = (1,0,0,0,1), $count = 1
+# Output: 1
+#
+# You are asked to replace only one 0 as given count is 1.
+# We can easily replace middle 0 in the list i.e. (1,0,1,0,1).
+#
+# Example 2:
+#
+# Input: @numbers = (1,0,0,0,1), $count = 2
+# Output: 0
+#
+# You are asked to replace two 0's as given count is 2.
+# It is impossible to replace two 0's.
+#
+# Example 3:
+#
+# Input: @numbers = (1,0,0,0,0,0,0,0,1), $count = 3
+# Output: 1
+
+use Modern::Perl;
+
+sub can_place {
+ my($nums, $count) = @_;
+ my $str = join '', @$nums;
+ for (1 .. $count) {
+ if (!($str =~ s/000/010/)) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+@ARGV==2 or die "usage: ch-2.pl 1,0,... n\n";
+my @nums = split /,/, shift;
+my $count = shift;
+say can_place(\@nums, $count);
+
+
diff --git a/challenge-215/paulo-custodio/t/test-1.yaml b/challenge-215/paulo-custodio/t/test-1.yaml
new file mode 100644
index 0000000000..6c78cec738
--- /dev/null
+++ b/challenge-215/paulo-custodio/t/test-1.yaml
@@ -0,0 +1,15 @@
+- setup:
+ cleanup:
+ args: abc xyz tsu
+ input:
+ output: 1
+- setup:
+ cleanup:
+ args: rat cab dad
+ input:
+ output: 3
+- setup:
+ cleanup:
+ args: x y z
+ input:
+ output: 0
diff --git a/challenge-215/paulo-custodio/t/test-2.yaml b/challenge-215/paulo-custodio/t/test-2.yaml
new file mode 100644
index 0000000000..b3a794a3b1
--- /dev/null
+++ b/challenge-215/paulo-custodio/t/test-2.yaml
@@ -0,0 +1,15 @@
+- setup:
+ cleanup:
+ args: 1,0,0,0,1 1
+ input:
+ output: 1
+- setup:
+ cleanup:
+ args: 1,0,0,0,1 2
+ input:
+ output: 0
+- setup:
+ cleanup:
+ args: 1,0,0,0,0,0,0,0,1 3
+ input:
+ output: 1