Stuart Strickland · Jan 3, 2023 go to post

I had to do something like this a few years ago to add a digital signature to a message in XML format. If I remember correctly you have to get your object into a %XML.Document which works in conjunction with %XML.Node. %XML.Node is used to traverse the %XML.Document to get to the section you want in canonical form. Then you pass the Node to ##class(%XML.Writer).Canonicalize(Node) to get the XML as a string which is then passed to the encryption function you use to get your digest/signature. You can pass the whole document or just a subsection to the canonicalize function.

I can't say if it's the only or best way to do it but it was sufficiently quick enough to handle thousands of messages per minute.

Stuart Strickland · Dec 14, 2022 go to post

Discourage the use of hard coded breaks in the first place! There are alternatives.

  • Use ZBREAK Tag+OffSet^Routine to set a break point just for the current process
  • Use the debugger in Cache´ Studio (under the Debug menu and attach to a process)
  • George James has a decent debugger in Serenjii
  • Put something in your processes to remove hard coded break points when you promote code through the development cycle (you can automate this if you try)
Stuart Strickland · Nov 24, 2022 go to post

I've seen some places where the ^ROUTINE global is fully or partially deleted after compilation. I suspect that the same would work for ^rMAC or ^oddDEF - you'd have to test it. One company that I'm aware of encrypts the ^ROUTINE global after compilation which has the added "benefit" of making it extra difficult to debug errors! If you really want to hide your code you could compile the code on an identical system and then just deploy the object code.

Stuart Strickland · Nov 11, 2022 go to post

I had the same thoughts but decided to make assumptions that allowed me to code using the least characters. If you cater for the tricky stuff you'd probably have to double the length of your code.

Stuart Strickland · Nov 11, 2022 go to post

Shaved off another character

ClassMethod Order(As %String) As %String
{
s=$p(a," ",$i(i)),n=$zstrip(s,"*a"),$p(z," ",n)=$tr(s,n) 1:$g(z)
}
 

Stuart Strickland · Nov 11, 2022 go to post

Make that 77 with this:

ClassMethod Order(As %String) As %String
{
 f{s=$p(a," ",$i(i)),n=$zstrip(s,"*a"),$p(z," ",n)=$tr(s,n) ret:'$g(z)}
}
 

Stuart Strickland · Nov 11, 2022 go to post

Nice generation of a pandigital number with 1e20/17.

Can save a character with $zpi_0

ClassMethod Order(As %String) As %String
{
 r=i=1:1:5e5{s=$p(a," ",i),w=$tr(s,$zpi_0),$p(r," ",$tr(s,w))=wr
}

This was mine also at 78:

ClassMethod Order(As %String) As %String
{
 i=1:1 s=$p(a," ",i),n=$zstrip(s,"*a"),$p(z," ",n)=$tr(s,n) ret:'$g(z)
}
 

Stuart Strickland · Sep 1, 2022 go to post

And if you find the job is starting (from evidence provided by $T and $ZCHILD) then ensure you have an error trap in your class to catch why it's falling over. At the very least make it Set ^tempGlo($H)=$ZE or DO ^%ET when it encounters an error.

If you can't edit the class for any reason then JOB a routine that you can edit and make that call your class after setting up an error trap.

Stuart Strickland · Aug 16, 2022 go to post

#1) Why did you choose to become a software engineer / developer?

I was at University when introduced to COBOL. Found it really easy and natural while my friends appeared to struggle. Some of them offered to pay me to write their coursework. The first program I sold was to the nephew of the manager of a Scottish pop group (The Bay City Rollers). He gave me a 3 month supply of potatoes for one program! When I found I could make an easy living writing code there was no looking back. (And I haven't been paid in potatoes since I was a student)

#2) How and when did you start to generate a "flow state of mind" during your career?

We were taught Jackson Structured Programming at Uni. A short time drawing on a large sheet of paper how a program should flow works wonders for getting it right first time. (Right first time saves money). If you start off as you mean to go on it very quickly becomes the natural way to do things. I don't use JSP now but its basic principles are the basis for how my programming mind thinks. I'd say you should pick a programming methodology and stick to it until you are competent. As @Robert Cemper said, learn your language fluently.
 

#3)What are recommended habits inside and outside, during you own time and during
your work time, to be focused during your coding session and daily tasks?

Write re-useable code that can be called to do small or big repetitive tasks.

Don't try to write anything that is all things to all people. If you write code well then it should be easy to add new functionality.

When you are happy that your code is doing the job, don't seek the approval of someone with a big ego - they will try to make you re-write it. And related to this, if someone else writes code that works yet you don't understand it realise that it's your time to learn, don't try to make them change it.

Comment your code then go back and re-read it the next day. If it doesn't make sense to you then it won't make sense to anyone else. Write the gist of a program at the top of the code.

Your code is going to have your name on it for years. Make it a good advert for your skills.

If you find something repetitive and/or annoying, automate it. It might cost you a couple of hours in the short term but in the long term it will save you years.

Don't allow yourself to be distracted by valueless tripe while at work (eg social media)

Find a way to make things fun and enjoy yourself.

Stuart Strickland · Jul 8, 2022 go to post

199 if I steal your ideas

 i=1:1:$l(a){x=$lfs("A,4,@,B,|3,8,C,(,<,E,3,€,G,9,6,I,|,],K,|<,|{,L,1,£,O,0,*,S,5,$,T,7,+,X,><,}{,Z,2,~/_"),c=$e(a,i),p=$lf(x,$$$UPPER(c)) s:k(p)=3-$g(k(p),2),c=$li(x,p+k(p)) r=$g(r)_cr
 

Stuart Strickland · Jul 8, 2022 go to post

I did have a 220 in one line but your answer is superb.

A=$lfs("A,4,@,,B,|3,8,,C,(,<,,E,3,€,,G,9,6,,I,|,],,K,|<,|{,,L,1,£,,O,0,*,,S,5,$,,T,7,+,,X,><,}{,,Z,2,~/_") i=1:1:$l(x){L=$E(x,i),P=$lf(A,$zu(28,L,5))+3 s:P#4=0 $LI(A,P)='$LG(A,P),$e(x,i)=$LI(A,P-1-$LG(A,P))x

Stuart Strickland · Jul 4, 2022 go to post

Here's my answer. Looks like I'm playing on my own. 242 Chars, not including comments.

/// For rules see https://community.intersystems.com/post/code-golf-zcvtstr-leet
/// Build a list where each letter that is to be replaced
/// is followed in the list by its two possible replacements.
/// The next piece indicates which was the last replacement used.
/// Note: Only uppercase letters are in the list so need to
/// translate before searching, using $ZU(28,L,5) to translate.
/// For each character in the input string find its list position.
/// Use $LISTFIND to find its position.
/// Test the position found with Position-1#4
/// Not found characters return 0, and 0-1#4 is non zero so do nothing with them
/// Positions 1,5,9,11,15,19,23,27,31,35,39,43, and 47 contain characters
/// that must be replaced. Position-1#4 is zero for these.
/// No test case or rule for any of the destination symbols already existing in
/// the input string. Introducing that will complicate things as would need to test
/// character before and after current one to try to avoid double letters.
/// When each character is found change the last used replacement indicator.
/// Then return the replacement characters.
/// If the replacement characters contained any of the replaceable characters
/// then you would have to work backwards from the end of the input string
/// to avoid getting in a loop. This would cost 1 character.
ClassMethod Convert(As %String) As %String
{
A=$lfs("A,4,@,,B,|3,8,,C,(,<,,E,3,€,,G,9,6,,I,|,],,K,|<,|{,,L,1,£,,O,0,*,,S,5,$,,T,7,+,,X,><,}{,,Z,2,~/_")
i=1:1:$l(x){$E(x,i)=$$r($e(x,i))x
r(L)P=$lf(A,$zu(28,L,5)) q:P-1#4 $LI(A,P+3)='$LG(A,P+3) $LI(A,P+2-$LG(A,P+3))
}

Stuart Strickland · Jul 1, 2022 go to post

I've got an answer in 242 characters but I think there's a mistake in the test case:

Do $$$AssertEquals(##class(CodeGolf.Leet).Convert("no no no no "), "n0 n* n0 n*")

Do $$$AssertEquals(##class(CodeGolf.Leet).Convert("Iris"), "|R]5")

The "n" doesn't get converted to uppercase but the "r" does. Surely it should be

Do $$$AssertEquals(##class(CodeGolf.Leet).Convert("Iris"), "|r]5")

and there's a rogue space at the end of "no no no no ".

Stuart Strickland · May 20, 2022 go to post

185 - with an unusual use of $PIECE instead of $SELECT to save 2 characters, which would also shorten yours to 184

c=",",x=$p(a,c,$i(i)) q:x="" f{y=$p(a,c,$i(j)+i),g=$g(g,y-x) q:j*g+x'=y  l="-"_yg=$zabs(g),$p(o,c,$i(p))=s:2-'g<o=o_$s(g:l_$p("/"_g,c,g>1),1:"*"_j),i=i+j-1 g,1

i=1:1:2e6{x=",",a=$p(s,x,i),d=$p(s,x,i+1)-c=1:1{q:d*c+a'=$p(s,x,i+c)q=$zabs(d),v=$s(c>2&d:"-"_(c-1*d+a)_$p("/"_q,x,q>1),c>1&'d:"*"_c,1:0) s:v'=0 $p(s,x,i,i+c-1)=a_vs

Stuart Strickland · May 19, 2022 go to post

Hi Vitaliy,

Out of curiosity I had a go at this. I can't get under 188 using my own ideas and code:

c=",",x=$p(a,c,$i(i)) q:x="" f{y=$p(a,c,$i(j)+i),g=$g(g,y-x) q:j*g+x'=y  l=yg=$zabs(g),$p(o,c,$i(p))=s:2-'g<jo=o_$s(g:"-"_l_$s(g>1:"/"_g,1:""),1:"*"_j),i=i+j-1g,1

You could shave 2 characters off each of your versions by having a variable contain the comma:

x=",",a=$p(s,x,$i(i)),d=$p(s,x,i+1)-c=1:1{q:d*c+a'=$p(s,x,i+c)q=$zabs(d),v=$s(c>2&d:"-"_(c-1*d+a)_$s(q=1:"",1:"/"_q),c>1&'d:"*"_c,1:0) s:v'=0 $p(s,x,i,i+c-1)=a_q:a="" a

Stuart Strickland · May 13, 2022 go to post

Ah, you are correct. My test cases for a fail were of differing lengths. Doh!

What is needed is a simple command to sort the letters into a consistent order before comparison.

Stuart Strickland · May 13, 2022 go to post

I think you should have kept going Alexey. I was trying to ignore this page but it can be addictive. With a little tweak the $TRANSLATE works AND solves the problem in 64 characters and takes the lead.

ClassMethod Detector(As %String, As %String) As %Boolean
{
 a=$zu(28,a,6),b=$zu(28,b,6) $l($tr(a,b,a))=$l($tr(b,a,b))
}
 

Stuart Strickland · May 12, 2022 go to post

Don't know why I didn't think of this earlier - perhaps it's been a long time since we were forced to minimize characters when writing code. Here's a version that only uses 65.

ClassMethod Detector3(As %String, As %String) As %Boolean
{
 i=91:-1:0{q:$$r(a)-$$r(b)'i
r(x)$l($zu(28,x,6),$c(i))
}

Stuart Strickland · May 12, 2022 go to post

I should pay more attention to the $$$MACROs in future!

$ZU(28 is great, it's a shame that Intersystems don't document enough $ZU functions?

The FOR loops that range from 0 to 90/91 don't work with anagrams that contain different numbers of spaces because i=32 checks spaces. E.g. w ##class(CodeGolf.Anagram).Detector("New York Times","monkeys write")

Ah! I stand corrected. Just read %SYSTEM.Util  ALPHAUP removes spaces!

Stuart Strickland · May 12, 2022 go to post

Here is the code for a 69 character answer

/// 73 characters:
/// f x=a,b{f i=65:1:90{s $li(x(x),i)=$l($zcvt(x,"u"),$c(i))}} q x(a)=x(b)
/// 
/// but this one is 69:
/// 
/// loop ascii number from "A" to "Z" and "[", that's 65 to 91
/// if count of each letter in each string is different then quit loop
/// if loop reached 91 then loop completed and letter counts must be same in both strings
/// if loop didn't reach 91 then must be counts must be different
ClassMethod Detector(As %String, As %String) As %Boolean
{
 i=65:1:91{q:$$r(a)'=$$r(b)i=91
r(x)$l($zcvt(x,"u"),$c(i))
}
 

Stuart Strickland · May 11, 2022 go to post

Hi Julius, you may have missed this note in the challenge:

Both arguments are case insensitive, only a-z, A-Z, no special characters except whitespace

Stuart Strickland · May 11, 2022 go to post

Also got down to 73. Can't get to 72. Changing $LIST to $EXTRACT is shorter but doesn't always work. Changing FOR loop to start from a single digit doesn't work because it would count space characters when it reaches 32.

x=a,b{i=65:1:90{$li(x(x),i)=$l($zcvt(x,"u"),$c(i))}} x(a)=x(b)

Stuart Strickland · May 11, 2022 go to post

Good work. I didn't know you could omit the space after the closing bracket on the line tag arguments.

Also didn't think using curly braces would save anything in this case but it looks like a new line counts as 2 characters with the code length calculator.

(And I think you may be waiting a long time for the Encoder game to close)

Stuart Strickland · May 10, 2022 go to post

Is there a little arrow below my 76 characters that will show my code when you click it? I can't see one under your 73 characters.

Stuart Strickland · May 10, 2022 go to post

You could save another character by using $Extract instead of $List but that won't work if there are more than 8 of the same letter in the anagram.

Stuart Strickland · May 10, 2022 go to post

How about 76 characters?

/// use $Length to count the occurrences of each letter!
/// store the counts in a list where the piece number is the ascii letter number
/// compare the lists for each word/phrase
ClassMethod Detector(As %String, As %String) As %Boolean
{
$$r(a)=$$r(b)
r(x) i=65:1:90 $li(y,i)=$l($zcvt(x,"u"),$c(i))
 y
}

Stuart Strickland · May 4, 2022 go to post

/// using first string replace with null each 'same' character found in second string leaving null or spaces
ClassMethod Detector(As %String, As %String) As %Boolean
{
$$r(a,b)_$$r(b,a)?." "
r(x,y) f  x=$replace(x,$e(y),"",,1,1),$e(y)="" ret:y="" x
}
/// 88 chars

Stuart Strickland · Apr 8, 2022 go to post

I'm a fan of beyond compare but it is not always available on some of the sites I log in to. I'm not a fan of RCMP. I've got a Diff program that works in a terminal session that I use when I log on to a Cache version 5 site. Feel free to take this and do what you want with it (.MAC routines can be found on ^rMAC):

 

Code

ZRDIFF(Master="",Minor="",N1="",N2="",Show=1,Debug=0,Diffs=0,LengthWidth="40;160",UDL=0) ; Poor man's side by side diff
                ; Knocked up to run a quick diff on an old Caché version 5 system where my nice diff program won't work
                ; Usage:
                ; Pass in 1st routine (arg1 is Master) to compare against itself (in another namespace) or against 2nd routine (arg2 is Minor)
                ; N1 and N2 are optional namespaces
                ; Alternatively, pass in a pair of arrays to compare in Master and Minor but ensure top node is populated with something.
                ; To compare classes use ##class(%Compiler.UDL.TextServices).GetTextAsArray(,ClassName,.Array) and do your own thing.
                ; Call it like $$^ZRDIFF(Routine1,Routine2,Namespace1,Namespace2,0,0,.Diffs) to get it to return a non-zero number indicating there's a difference
                ; Make Routine1 and Routine2 variables and pass by reference to get back the 2 routines as arrays
                ; The lines of code that are the same will be lined up
                ; Show=1 to display to terminal
                ; ScreenLength and width can be passed in if your terminal doesn't go to 160 columns.
                ; Pass in "24;80" if you like, it will work.
                ; Pass in "24;9999" if you are going to use the arrays and pass them to something else that looks after formatting
                ;
                I Master="" Q:$Q "99No routine supplied" Q
                I Minor="" M Minor=Master
                ;
                ; To compare across namespaces populate N1 and N2 with a valid namespace
                ; default is current namespace
                I N1="" S N1=$ZNSPACE
                I N2="" S N2=$ZNSPACE
                ;
                ; Written to run on a putty terminal so screen handling is a bit noddy
                ; Don't like the screen handling? You change it.
                ; Look for InverseOn and InverseOff as they are the variables used for highlighting differences
                ; THIS IS FOR A VT100 EMULATION
                I Show W $C(27)_"[8;"_LengthWidth_"t" ; screen length(40) and width(160)
                N HalfWidth,I,L,Left,R,Right,MASTER,MINOR,MI,RA,RB,R1,R2
                S HalfWidth=$P(LengthWidth,";",2)\2
                ; 
                ; Plan:
                ; Get 2 routines and normalise each line of code into 2 arrays so that
                ; all tab characters converted to spaces
                ; start of line is spaced out to 9 spaces (allows an 8 char tag), longer tags left alone
                ; Decide which of the two routines has more lines (left or right)
                ; Use the larger routine as the master routine
                ; Find all the lines of code in the minor routine that are identical to master and align them to the master
                ; but don't change the order of the lines, expand the arrays instead so that the similar lines match up
                ; Then lines broken into 78 character chunks so that the compare can be done side by side on a 160 wide screen
                ; 
                S RA=Master,RB=Minor
                M R1=Master,R2=Minor
                ; Obvsiously, not doing this if you passed in a pair of arrays
                I $D(R1)<10 K R1 M R1=^[N1]ROUTINE(RA,0)
                I $D(R2)<19 K R2 M R2=^[N2]ROUTINE(RB,0)
                I '$G(R1(0)) S R1(0)=$O(R1(""),-1) ; number of lines in routine
                I '$G(R2(0)) S R2(0)=$O(R2(""),-1)
                ;
                ; UDL means you passed in a class or two in UDL format so don't bother normalising it's already done
                ; If they are not, then don't pass UDL=1 !
                I 'UDL {
                                D Normalise(.R1)
                                D Normalise(.R2)
                }
                I R2(0)>R1(0) {
                                S MASTER="R2",MINOR="R1"
                                D Compare(.R2,.R1)
                }
                Else {
                                S MASTER="R1",MINOR="R2"
                                D Compare(.R1,.R2)
                }
                ; Getting the odd occurence where a pair of lines shows up next to each other claiming to be different when they are not
                ; Happens when ^ROUTINE has a missing line in the middle!
                ; so align them again
                S I=0
                F  {
                                S I=$O(Master(I)) q:'I
                                I Master(I)'="",$G(Minor(I))="" {
                                                ; they are the same
                                                I $G(Master(I-1))="",$G(Minor(I-1))=Master(I) S Minor(I)=Minor(I-1) K Master(I-1),Minor(I-1) continue
                                                I $G(Master(I+1))="",$G(Minor(I+1))=Master(I) S Minor(I)=Minor(I+1) K Master(I+1),Minor(I+1) continue
                                }
                }
                I Show {
                                S MI=0
                W #!,$S(MASTER="R1":$$LJ($$LJ(N1,15)_RA,HalfWidth)_$$LJ(N2,15)_RB,1:$$LJ($$LJ(N2,15)_RB,HalfWidth)_$$LJ(N1,15)_RA)
                                F  {
                                                S MI=$O(Master(MI)) Q:'MI
                                                S Left=$S(MASTER="R1":Master(MI),1:Minor(MI))
                                                S Right=$S(MASTER="R2":Master(MI),1:Minor(MI))
                                                I Left'=Right,$I(Diffs) S Diffs(MI)="" ; lines where something is different
                                                ; Convert routine into lines less than 80 characters long
                                                F  {
                                                                S L=$E(Left,1,HalfWidth-2)
                                                                S Left=$E(Left,HalfWidth-1,9999)
                                                                S R=$E(Right,1,HalfWidth-2)
                                                                S Right=$E(Right,HalfWidth-1,9999)
                                                                D Show(L,R)
                                                                q:Left_Right=""
                                                }
                                }
                }
                ; Just in case you passed in arrays by reference and are expecting the left and right hand routines to come back to you...
                K Left,Right
                I MASTER="R1" {
                                ; don't need to do anything - already the right way round
                }
                Else {
                                M Left=Master,Right=Minor
                                K Master,Minor
                                M Master=Left,Minor=Right
                }
                S Master=RA,Minor=RB
                q:$Q Diffs q
                ;
Normalise(ROU) N i,I,Line,N
                S i=0
                F I=1:1:ROU(0) {
                                S Line=$G(ROU(I)) ; need the $G because some old systems have missing lines of code! Don't ask me.
                                ; swap tabs for spaces
                                S Line=$TR(Line,$c(9)," ")
                                ; if first character is a " " make it 9 spaces
                                I $E(Line)=" " S $E(Line)=" "
                                ; or if it's a tag with no arguments, pad it out to 9 spaces
                                I $P(Line," ")?1(1"%",1AN).AN {
                                                S $P(Line," ")=$$LJ($P(Line," "),9)
                                }
                                // or if it has arguments and is (reasonable guess) less than 8 characters, pad out to 9 spaces, could fail if default values for arguments include spaces or braces
                                ElseIf $L($P(Line,")"))<8,$P(Line," ")?1(1"%",1AN).AN1"(".e1") " {
                                                S $P(Line," ")=$$LJ($P(Line," "),9)
                                } // leave everything else alone
                                ;
                                S N($i(i))=Line
                }
                ; Now the noralised routine is in N() move it to R1() or R2()
                S N(0)=ROU(0)
                K ROU M ROU=N
                Q
R1LinesUpLater(I,i) ; parameter passed by value so will return to orignal value on quit
                N OK
                S OK=0
                F  {
                                S I=$O(ROU1(I)) q:'I
                                F  {
                                                S i=$o(ROU2(i)) Q:'i  ; NON-NUMERIC CATCHES THINGS LIKE "LANG"
                                                ; 2 consecutive lines the same will have to do
                                                I ROU1(I)=ROU2(i) {
                                                                I ($G(ROU1(I+1))=$G(ROU2(i+1))) S OK=1 Q
                                                }
                                }
                                Q:OK!'i
                }
                Q OK
R2LinesUpLater(I,i) ; parameter passed by value so will return to orignal value on quit
                N OK
                S OK=0
                F  {
                                S i=$O(ROU2(i)) q:'i
                                F  {
                                                S I=$o(ROU1(I)) Q:'I  ; NON-NUMERIC CATCHES THINGS LIKE "LANG"
                                                ; 2 consecutive lines the same will have to do
                                                I ROU1(I)=ROU2(i) {
                                                                I ($G(ROU1(I+1))=$G(ROU2(i+1))) S OK=1 Q
                                                }
                                }
                                Q:OK!'I
                }
                Q OK
                ; left justify
LJ(x,n) q x_$j("",n-$l(x))
                ;
Part(x,ION,IOFF) w:x'="" ION w x w:x'="" IOFF q ""
Show(Left,Right) ; show the current row and also highlight when different
                N InverseOn,InverseOff
                S InverseOn=$s(Left=Right:"",1:$c(27,91)_"7m")
                S InverseOff=$s(Left=Right:"",1:$c(27,91)_"27m")
                W !
                W $$Part(Left,InverseOn,InverseOff)
                W ?HalfWidth
                W $$Part(Right,InverseOn,InverseOff)
                q
                ;
Compare(ROU1,ROU2) N I,i
                S (I,i,MI)=0
                F  {
                                S I=$O(ROU1(I),1,L) Q:'I    ; NON-NUMERIC CATCHES THINGS LIKE "LANG"
                                F  {
                                                S i=$o(ROU2(i),1,R) Q:'i  ; NON-NUMERIC CATCHES THINGS LIKE "LANG"
                                                ; 2 consecutive lines the same will have to do
                                                I ROU1(I)=ROU2(i) {
                                                                ; this works for the first line and last line
                                                                I ($G(ROU1(I+1))=$G(ROU2(i+1))) || (I=i) || $$R1LinesUpLater(I,i+1) || $$R2LinesUpLater(I+1,i) {
                                                                                ; move to a new array and make them line up
                                                                                S MI=MI+1
                                                                                S Master(MI)=ROU1(I),Minor(MI)=ROU2(i)
                                                                                D:$g(Debug) Show(Master(MI),Minor(MI))
                                                                                Q  ; go get next ROU1(I)
                                                                }
                                                                ; still here?
                                                                ; Then 2 lines the same but next lines are not and ROU1(I+1) doesn't have a matching line later in ROU2(i+1 onwards)
                                                                ; Move ROU1(I+1) to Master and increment I
                                                                S MI=MI+1
                                                                S Master(MI)=ROU1(I),Minor(MI)=ROU2(i)
                                                                D:$g(Debug) Show(Master(MI),Minor(MI))
                                                                q  ;S MI=MI+1,I=I+1,i=i-1 ; was nearly working but Quit works better
                                                                I I'>ROU1(0) {
                                                                                S Master(MI)=ROU1(I),Minor(MI)=""
                                                                                D:$g(Debug) Show(Master(MI),Minor(MI))
                                                                }
                                                                Q
                                                }
                                                ; got here then they are different
                                                ; if they line up later then show the line from ROU2 now
                                                I $$R1LinesUpLater(I,i+1) {
                                                                S MI=MI+1
                                                                S Master(MI)="",Minor(MI)=ROU2(i)
                                                                D:$g(Debug) Show(Master(MI),Minor(MI))
                                                                Q  ; go get next ROU1(I)
                                                }
                                                ; if they line up later then show the line from ROU1 now
                                                I $$R2LinesUpLater(I,i-1) {
                                                                S MI=MI+1,i=i-1
                                                                S Master(MI)=ROU1(I),Minor(MI)=""
                                                                D:$g(Debug) Show(Master(MI),Minor(MI))
                                                                Q  ; go get next ROU1(I)
                                                }
                                ; Still here?
                                ; Then 2 different lines, show them both
                                S MI=MI+1
                                S Master(MI)=ROU1(I),Minor(MI)=""
                                D:$g(Debug) Show(Master(MI),Minor(MI))
                                S MI=MI+1
                                S Master(MI)="",Minor(MI)=ROU2(i)
                                D:$g(Debug) Show(Master(MI),Minor(MI))
                                Q
                                }
                }
                q
                ;
TEST k  S r1="GT101",r2="GT101"
                D ZRDIFF(.r1,.r2,"APEX","TAPEX",1,0,.Diffs) Q