[[{“value”:”Welcome to another edition of Talsco Weekly IBM i Brief: 🔷 IBM Power servers get major AI upgrade. IBM Champions Recognized at TechXchange 📹. 2024 IBM i Year in Review: Key Highlights 📋. AI: 🔄 Lisp remains influential in programming history. 🤖 AI experts tackle trust and bias challenges. Career: 👔 IBM i Career preparedness
The post Talsco Weekly: Talent Gap: A Reality Check for IBM i Shops appeared first on IBM i (AS/400, RPG) Recruiting, Staffing & Consulting.”}]] Read More
Leveraging Open Source and Modern Technologies on IBM i with Andrew Youens at BENELUX POWER 2024 Remain Software
[[{“value”:”Recorded at BENELUX POWER 2024, Wim Jongman and Andrew Youens recorded an insightful podcast exploring cutting-edge technologies for IBM i.
This podcast was part of the larger event that featured expert speakers from around the world, including IBM Rochester development labs.”}]] Read More
VS Code for IBM i: SEP Debugging Prerequisites Alan Seiden
[[{“value”:”Code for i users have been showing great interest in Service Entry Point (SEP) debugging. SEP permits debugging of applications that run in a different job than the programmer’s job, such as a web server or prestart job. Available with Version 2 of the IBM Debug Service, SEP requires several prerequisites, including the IBM i Debug extension, PTFs, and Licensed Program Products (LPPs). This article focuses on the PTFs and LPPs, objects that are frequently overlooked. Required PTFs and LPPs Per the current Required PTFs section of the Code for i documentation, these are the required PTFs and LPPs: Host…
The post VS Code for IBM i: SEP Debugging Prerequisites appeared first on Seiden Group.”}]] Read More
Advent of Code 2024 day 7, RPG edition
[[{“value”:”This is the seventh day of Advent of Code, let’s solve today’s puzzles with RPG.
The puzzle as well as the input data are available here.
The code is available here.
Part 1
We are provided with a (stream) file containing a serie of equations with the operators missing. The operators can be plus or multiply, operations are done from left to right, there is no operator precedence.
The goal of part one is to calculate the sum of the equations that can be made true by adding operators.
As usual, we read the input file with QSYS2.IFS_READ_UTF8 table function.
We create the solve procedure that try to solve the equation by using either add or multiply for the two leftmost values and recursively calls itself with the rest of the values until no more values are left. It returns 0 if the current combination of operators doesn’t solve the equation or the result if it does.
We use dynamic arrays to store the values. Problem is you can’t pass dynamic (or even varying length) arrays to procedures so we do it the old fashion way by passing a fixed length array and a number of elements.
Since solve returns 0 if the equation can’t be solved, we can simply sum the result of solve.
Here is the RPG code for part 1:
**free
ctl-opt dftactgrp(*no); // We’re using procedure so we can’t be in the default activation group
dcl-pi *n;
input char(50);
end-pi;
dcl-pr solve int(20);
target int(20) value;
left int(20) value;
right int(5) dim(30) value;
rightElems int(5) value;
end-pr;
dcl-s fileName varchar(50);
dcl-s result int(20) inz(0);
dcl-s line varchar(1000);
dcl-s sum int(20);
dcl-s values int(5) dim(*auto:30);
dcl-s value varchar(5);
fileName = %trim(input);
// Read the input data from the IFS
exec sql declare c1 cursor for select cast(line as varchar(1000)) from table(qsys2.ifs_read_utf8(path_name => :fileName));
exec sql open c1;
// Read the first line
exec sql fetch from c1 into :line;
dow sqlcode = 0;
// read the sum
sum = %int(%left(line:%scan(‘:’:line)-1));
// read the terms
%elem(values) = 0;
for-each value in %split(%subst(line:%scan(‘:’:line)+1));
values(*next) = %int(value);
endfor;
result += solve(sum:values(1):%subarr(values:2):%elem(values)-1);
// Read the next line
exec sql fetch from c1 into :line;
enddo;
snd-msg *info ‘Result: ‘ + %char(result) %target(*pgmbdy:1); // Send message with answer
*inlr = *on;
return;
dcl-proc solve;
dcl-pi *n int(20);
target int(20) value;
left int(20) value;
right int(5) dim(30) value;
rightElems int(5) value;
end-pi;
if rightElems = 1;
if left+right(1) = target or left*right(1) = target;
return target;
else;
return 0;
endif;
else;
if solve(target:left+right(1):%subarr(right:2:rightElems-1):rightElems-1) > 0 or solve(target:left*right(1):%subarr(right:2:rightElems-1):rightElems-1) > 0;
return target;
else;
return 0;
endif;
endif;
end-proc;
Part 2
Part 2 is exactly the same as part 1 with an added operator: concatenation (
) which, as the name implies, concatenates numbers (12
95 = 1295).
We simply add a test for the concatenate operator to the program from part 1.
**free
ctl-opt dftactgrp(*no); // We’re using procedure so we can’t be in the default activation group
dcl-pi *n;
input char(50);
end-pi;
dcl-pr solve int(20);
target int(20) value;
left int(20) value;
right int(5) dim(30) value;
rightElems int(5) value;
end-pr;
dcl-pr concatenate int(20);
left int(20) value;
right int(20) value;
end-pr;
dcl-s fileName varchar(50);
dcl-s result int(20) inz(0);
dcl-s line varchar(1000);
dcl-s sum int(20);
dcl-s values int(5) dim(*auto:30);
dcl-s value varchar(5);
fileName = %trim(input);
// Read the input data from the IFS
exec sql declare c1 cursor for select cast(line as varchar(1000)) from table(qsys2.ifs_read_utf8(path_name => :fileName));
exec sql open c1;
// Read the first line
exec sql fetch from c1 into :line;
dow sqlcode = 0;
// read the sum
sum = %int(%left(line:%scan(‘:’:line)-1));
// read the terms
%elem(values) = 0;
for-each value in %split(%subst(line:%scan(‘:’:line)+1));
values(*next) = %int(value);
endfor;
result += solve(sum:values(1):%subarr(values:2):%elem(values)-1);
// Read the next line
exec sql fetch from c1 into :line;
enddo;
snd-msg *info ‘Result: ‘ + %char(result) %target(*pgmbdy:1); // Send message with answer
*inlr = *on;
return;
dcl-proc solve;
dcl-pi *n int(20);
target int(20) value;
left int(20) value;
right int(5) dim(30) value;
rightElems int(5) value;
end-pi;
if rightElems = 1;
if left+right(1) = target or left*right(1) = target or concatenate(left:right(1)) = target;
return target;
else;
return 0;
endif;
else;
if solve(target:left+right(1):%subarr(right:2:rightElems-1):rightElems-1) > 0 or solve(target:left*right(1):%subarr(right:2:rightElems-1):rightElems-1) > 0
or solve(target:concatenate(left:right(1)):%subarr(right:2:rightElems-1):rightElems-1) > 0;
return target;
else;
return 0;
endif;
endif;
end-proc;
dcl-proc concatenate;
dcl-pi concatenate int(20);
left int(20) value;
right int(20) value;
end-pi;
return %int(%char(left)+%char(right));
end-proc;”}]] Read More
Advent of Code 2024 day 6, RPG edition
[[{“value”:”This is the sixth day of Advent of Code, let’s solve today’s puzzles with RPG.
The puzzle as well as the input data are available here.
The code is available here.
Part 1
We are provided with a (stream) file containing a map of an area containing obstacles that is patroled by a guard. The guard walks straight until he encounters an obstacle. When that happens, he turns 90° right and walks straight. This repeats until he walk outs of the mapped area.
The goal of part one is to calculate how many positions the guard visits until he leaves the area.
Like on day 4, we read and aggregate all the lines of the map with QSYS2.IFS_READ_UTF8 table function and LISTAGG SQL function.
We store the current guard direction in an integer variable: 1 means up, 2 means right, 3 means down and 4 means left. Turning right means calculating the reminder of the division by 4 and adding 1.
When moving, we check if the next position is an obstacle. If this is the case, we turn right. Otherwise, we move to the next position and mark is with ‘X’.
We loop until the guard gets out of the area.
We count the visited positions using a regular expression and the REGEXP_COUNT SQL function.
Here is the RPG code for part 1:
**free
ctl-opt dftactgrp(*no); // We’re using procedure so we can’t be in the default activation group
dcl-pi *n;
input char(50);
end-pi;
dcl-s fileName varchar(50);
dcl-c WIDTH 130;
dcl-c HEIGHT 130;
dcl-c LENGTH const(16900);
dcl-s data char(LENGTH);
dcl-s result int(10) inz(0);
dcl-s line int(10);
dcl-s col int(10);
dcl-s index int(10);
dcl-s pCurChar pointer;
dcl-s curChar char(1) based(pCurChar);
dcl-s pNextChar pointer;
dcl-s nextChar char(1) based(pNextChar);
dcl-s direction int(5) inz(1); // 1-up, 2-right, 3-down, 4-left
fileName = %trim(input);
// Read the input data from the IFS concatenating all lines
exec sql select listagg(line) into :data from table(qsys2.ifs_read_utf8(path_name => :fileName));
// Find the initial position
index = %scan(‘^’:data);
line = %div(index-1:WIDTH) + 1;
col = %rem(index-1:WIDTH) + 1;
pCurChar = %addr(data) + index – 1; // Mark initial position as used.
curChar = ‘X’;
// We loop until we exit the area
dou (line = 1 and direction = 1) or (col = WIDTH and direction = 2) or (line = HEIGHT and direction = 3) or (col = 1 and direction = 4);
select direction; // Move pNextChar to the next position depending on the direction
when-is 1; // right
pNextChar = pCurChar – WIDTH;
when-is 2; // down
pNextChar = pCurChar + 1;
when-is 3; // left
pNextChar = pCurChar + WIDTH;
when-is 4; // up
pNextChar = pCurChar – 1;
endsl;
if nextChar <> ‘#’; // If nextChar is not an obstacle, we proceed
pCurChar = pNextChar;
line = %div(pCurChar-%addr(data):WIDTH) + 1;
col = %rem(pCurChar-%addr(data):WIDTH) + 1;
curChar = ‘X’; // Mark the position as visited
else; // If next is an obstacle, we don’t move and rotate right
direction = %rem(direction:4) + 1;
endif;
enddo;
// Use regular expression to count the visited positions
exec sql select REGEXP_COUNT(:data, ‘X’) into :result from sysibm.sysdummy1;
snd-msg *info ‘Result: ‘ + %char(result) %target(*pgmbdy:1); // Send message with answer
*inlr = *on;
return;
Part 2
In part 2, we’ll use the same input as in part 1.
In this part, we want to send the guard into a loop by adding one obstacle.
The goal is to calculate in how many different positions we can add one obstacle to send the guard into a loop.
We loop over all the positions that are not already an obstacle or the initial guard position and check wether adding an obstacle at this position sends the guard into a loop or not.
The trickiest part is to detect the loop. For that, we change the way we mark a visited position: instead of marking it with an X, we mark it with the direction of the guard (1, 2, 3 or 4). If we visit a position with the same direction as previously, it means that the guard is in a loop. If the guard gets out of the area, it means it’s not in a loop.
**free
ctl-opt dftactgrp(*no); // We’re using procedure so we can’t be in the default activation group
dcl-pi *n;
input char(50);
end-pi;
dcl-pr isLoop ind end-pr;
dcl-s fileName varchar(50);
dcl-c WIDTH 130;
dcl-c HEIGHT 130;
dcl-c LENGTH const(16900);
dcl-s data char(LENGTH);
dcl-s dataOrig char(LENGTH);
dcl-s result int(10) inz(0);
dcl-s line int(10);
dcl-s col int(10);
dcl-s index int(10);
dcl-s i int(10);
dcl-s pCurChar pointer;
dcl-s curChar char(1) based(pCurChar);
dcl-s pNextChar pointer;
dcl-s nextChar char(1) based(pNextChar);
dcl-s direction int(5) inz(1); // 1-up, 2-right, 3-down, 4-left
fileName = %trim(input);
// Read the input data from the IFS concatenating all lines
exec sql select listagg(line) into :dataOrig from table(qsys2.ifs_read_utf8(path_name => :fileName));
for i = 1 to LENGTH; // Loop over all the positions of the map
pNextChar = %addr(dataOrig) + i -1;
if nextChar = ‘.’; // We only add an obstacle if the position is empty
direction = 1; // Reset direction to up
data = dataOrig; // Reset the map to the original map
pNextChar = %addr(data) + i -1;
nextChar = ‘#’; // Add the obstacle
if isLoop(); // If the guard enters a loop, we add 1 to the result
result += 1;
endif;
endif;
endfor;
snd-msg *info ‘Result: ‘ + %char(result) %target(*pgmbdy:1); // Send message with answer
*inlr = *on;
return;
dcl-proc isLoop;
dcl-pi *n ind;
end-pi;
// Find the initial position
index = %scan(‘^’:dataOrig);
line = %div(index-1:WIDTH) + 1;
col = %rem(index-1:WIDTH) + 1;
// Mark the initial position with the original direction (up)
pCurChar = %addr(data) + index – 1;
curChar = ‘1’;
// Loop until we exit the area
dou (line = 1 and direction = 1) or (col = WIDTH and direction = 2) or (line = HEIGHT and direction = 3) or (col = 1 and direction = 4);
select direction;
when-is 1;
pNextChar = pCurChar – WIDTH;
when-is 2;
pNextChar = pCurChar + 1;
when-is 3;
pNextChar = pCurChar + WIDTH;
when-is 4;
pNextChar = pCurChar – 1;
endsl;
if nextChar = %char(direction); // If the next position was already visited with the same direction, then we’re in a loop, we exit and return *on
return *on;
endif;
if nextChar <> ‘#’;
pCurChar = pNextChar;
line = %div(pCurChar-%addr(data):WIDTH) + 1;
col = %rem(pCurChar-%addr(data):WIDTH) + 1;
curChar = %char(direction);
else;
direction = %rem(direction:4) + 1;
endif;
enddo;
return *off; // If the guard exits the area, that means it’s not a loop
end-proc;”}]] Read More