# Compound Interest… with Wizard Money

Gringotts isn't just a vault, but a reputable financial institution and wizards need loans too. Since you don't want to be screwed over by the Gringotts goblins, you decided it would be a good idea to write a program to calculate interest. Interest is compounded only yearly.

Your task is to calculate total owed amount after interest given the principal, interest rate, and time (whole years), operating in whole denominations of wizard money, rounding down to the nearest whole Knut. There are 29 Bronze Knuts in a Silver Sickle and 17 Sickles in a Gold Galleon.

## Example

Loan taken out:
23 Knuts
16 Sickles
103 Galleons
@ 7.250%
For 3 years

Total owed after interest:
24 Knuts
4 Sickles
128 Galleons

## Notes and Rules

• Input and output may be in any convenient format. You must take in Knuts, Sickles, Galleons, interest rate, and time. All but interest rate will be whole numbers. The interest rate is in increments of 0.125%.
• Input money is not guaranteed to be canonical (i.e. you can have 29 or more Knuts and 17 or more Sickles.)
• Output must be the canonical representation. (i.e. less than 29 Knuts and less than 17 Sickles)
• Totals owed, up to 1,000 Galleons, should be accurate to within 1 Knut per year of interest when compared with arbitrary precision calculations.
• You may round down after each year of interest or only at the end. Reference calculations can take this into account for accuracy checks.

Happy golfing!

• Can we take the interest rate as a decimal instead of a percentage? (e.g., 0.0725 instead of 7.25) – Shaggy Mar 2 at 19:44
• @Shaggy I would also like to know this – senox13 Mar 2 at 21:01
• If the loan is exactly 1 Knut, and the interest is 99% per year, and the term is 1 year, should the result be "1 Knut" or "2 Knuts"? – Chas Brown Mar 2 at 23:34
• In other words, please clarify the mathematical meaning of the phrase rounding down – senox13 Mar 2 at 23:38
• @ChasBrown: 1 Knut. Truncate/floor function to the nearest whole Knut. – Beefster Mar 3 at 4:16

# R, 70 62 bytes

function(d,i,y)(x=d%*%(a=c(1,29,493))*(1+i)^y)%/%a%%c(29,17,x)

Try it online!

Takes input as d: deposit in knuts, sickles, galleons; i: interest rate as decimal; y: years. Outputs final deposit in knuts, sickles, galleons. Thanks to @Giuseppe for using matrix multiplication to save some bytes (and pointing out how to avoid the need to wrap at 1e99).

• I don't know R; what does having them wrap around win you? – dfeuer Mar 4 at 17:53
• @dfeuer they’re taken mod 1e99, so if your galleons get that high they’ll drop to zero – Nick Kennedy Mar 4 at 21:40
• What I'm wondering is what you gain by taking them mod 1e99. – dfeuer Mar 4 at 21:52
• Most R functions are vectorised. In this case, I'm passing the output through the %% function, which is mod. Ideally, I'd like to leave the galleons alone, but taking a number mod infinity returns NaN, and so I've just used a really large number (but one that is small in bytes). The alternatives I've come up with are longer (e.g. [tio.run/##JYrLCsIwEEV/… Try it online!]) – Nick Kennedy Mar 4 at 22:26
• @NickKennedy you could do 9e99 as well...Also, you can golf down to 63 bytes – Giuseppe Mar 4 at 22:50

# Python 3.8 (pre-release), 7574 71 bytes

-1 bytes thanks to @EmbodimentofIgnorance
-3 bytes thanks to @xnor

This takes Knuts, Sickles, and Galleons as ints, interest as a float (decimal, not percentage), and years as an int. It returns a tuple containing the number after interest of Knuts, Sickles, and Galleons, respectively.

lambda K,S,G,R,Y:((k:=int((K+G*493+S*29)*(1+R)**Y))%29,k//29%17,k//493)

Usage:

>>> print(I(23,16,103,0.0725,3))
(24, 4, 128)

Try it online!

• Good catch. Updating answer – senox13 Mar 2 at 21:58
• The question says operating in whole denominations of wizard money, rounding down. I took rounding down to mean chop off everything after the decimal point. Using the header definitely sounds like an easier way to do things. I'll do that for future posts, thanks – senox13 Mar 2 at 22:55
• That sounds a lot more like "truncating" than "rounding"; but I have asked the OP for clarification (because nit-picking is the name of the game here at PPCG :) ). – Chas Brown Mar 2 at 23:35
• I don't disagree with you, that's just the meaning I've always seen used for rounding down, because you always round to the integer below your result. Otherwise it's just normal rounding. Letting OP decide is a good idea – senox13 Mar 2 at 23:41
• FYI, a useful trick to make anonymous functions testable on TIO is to put I\= in the header like this. Also, it looks like k//29//17 can be k//493. – xnor Mar 3 at 0:28

# APL+WIN, 37 28 26 bytes

⌊a⊤((a←0 17 29)⊥⎕)×(1+⎕)*⎕

2 bytes saved thanks to lirtosiast

Try it online! Courtesy of Dyalog Classic

Explanation:

(1+⎕)*⎕ prompts for years followed by decimal interest rate and calculates
compounding multiplier

((a←0 17 29)⊥⎕) prompts for Galleons, Sickles and Knuts and converts to Knuts

⌊a⊤ converts back to Galleons, Sickles and Knuts and floor
after applying compound interest.
• ⌊a⊤(⎕⊥⍨a←0 17 29)×⎕*⍨1+⎕ for 24? – lirtosiast Mar 3 at 6:32
• @lirtosiast Thanks but I am afraid my ancient APL+WIN interpreter does not have the ⍨ function. By all means submit this as your own APL solution. – Graham Mar 3 at 6:45
• @lirtosiast Thanks again I have taken the 2 bytes resulting from the assignment to a. – Graham Mar 3 at 7:05

# Perl 6, 47 bytes

((1+*)*** *(*Z*1,29,493).sum+|0).polymod(29,17)

Try it online!

I'm surprised I managed to get this into an anonymous Whatever lambda! Especially the part where it's more *s than anything else. Takes input as interest rate (e.g. 0.0725), years, [Knuts, Sickles, Galleons] and returns a list of currencies in the same order.

### Explanation:

(1+*)           # Add one to the interest rate
***        # Raise to the power of the year
*      # And multiply by
(*Z*1,29,493).sum      # The number of Knuts in the input
+|0   # And floor it
(                              ).polymod(29,17)   # Get the modulos after divmoding by 29 and 17
• I'm surprised you didn't come up with a way to also get the number of Knuts/Sickles/Galleons also to fit into whatevers. Then it'd just be eh, like ************************* ;-) – guifa Mar 7 at 3:18
• @guifa The Whatevers are the inputs, so there can only really be 3 of them (though I can split up the currency input for some more *s but more bytes). The rest of the *s are from multiplication (*) and exponentials (**) – Jo King Mar 7 at 3:23
• I meant if you got the conversion rates (the 29/17 number) into them too. But of course it was a joke because you need to use those numbers more than once. Sorry if my humor didn't go through – guifa Mar 7 at 3:26

# Jelly, 29 bytes

÷ȷ2‘*⁵×÷¢S×¢d®U1¦Ṫ€Ḟ

A full program accepting arguments: rate; [Galleons, Sickles, Knuts]; years.
Prints [Galleons, Sickles, Knuts].

Try it online!

Floors at the end of the entire term.
÷ȷ2 may be removed if we may accept the rate as a ratio rather than a percentage.

### How?

“¢×ø‘    - list of code-age indices = [1,17,29]
©   - (copy this to the register for later use)
\ - reduce by:
×  -   multiplication  = [1,17,493]

ȷ2                  - 10^2 = 100
÷                    - divide = rate/100
‘                 - increment = 1+rate/100
⁵               - 5th command line argument (3rd input) = years
*                - exponentiate = (1+rate/100)^years --i.e. multiplicand
×              - multiply (by the borrowed amounts)
÷             - divide (all amounts in Galleons)
S           - sum (total Galleons owed)
×          - multiply (total owed in each of Galleons, Sickles, Knuts)
®       - recall from register = [1,17,29]
d        - divmod (vectorises) = [[G/1, G%1], [S/17, S^17], [K/17, K%17]]
U1¦    - reverse first one = [[G%1, G/1], [S/17, S%17], [K/17, K%17]]
Ṫ€  - tail €ach = [G/1, S%17, K%17]
Ḟ - floor (vectorises)

# Intel 8087 FPU assembly, 86 bytes

d9e8 d906 7f01 dec1 8b0e 8301 d9e8 d8c9 e2fc df06 7901 df06 8701 df06
7b01 df06 8501 df06 7d01 dec9 dec1 dec9 dec1 dec9 9bd9 2e89 01df 0687
01df 0685 01d9 c1de c9d9 c2d9 f8d8 f2df 1e7b 01d8 fadf 1e7d 01d9 c9d9
f8df 1e79 01

Unassembled and documented:

; calculate P+I of loan from wizard
; input:
;   G: number of Galleons (mem16)
;   S: number of Sickles (mem16)
;   K: number of Knuts (mem16)
;   R: interest rate (float)
;   T: time in years (mem16)
;   GS: Galleons to Sickles exchange rate (mem16)
;   SK: Sickles to Knuts exchange rate (mem16)
; output:
;   G: number of Galleons (mem16)
;   S: number of Sickles (mem16)
;   K: number of Knuts (mem16)
WIZ_INT_CALC    MACRO   G, S, K, R, T, GS, SK
LOCAL   LOOP_EXP
; - calculate interet rate factor
FLD   R         ; load interest rate
FADD            ; ST = rate + 1
MOV   CX, T     ; Exponent is count for loop
FLD1            ; load 1 into ST as initial exponent value
LOOP_EXP:           ; loop calculate exponent
FMUL  ST,ST(1)  ; multiply ST = ST * ST(1)
LOOP  LOOP_EXP
; - convert demonimations to Knuts
FILD  K         ; load existing Knuts
FILD  SK        ; load Sickles to Knuts rate
FILD  S         ; load existing Sickles
FILD  GS        ; load Galleons-to-Sickles exchange rate
FILD  G         ; load existing Galleons
FMUL            ; multiply galleons to get sickles
FMUL            ; multiply sickles to get knuts
FMUL            ; calculate P+I (P in Knuts * Interest factor)
; - redistribute demonimations to canonical form
FLDCW  FRD      ; put FPU in round-down mode
FILD   SK       ; load Sickles to Knuts rate
FILD   GS       ; load Galleons-to-Sickles exchange rate
FLD    ST(1)    ; copy Galleons-to-Sickles exchange rate to stack for later
FMUL            ; multiply to get Galleons-to-Knuts rate
FLD    ST(2)    ; push original total Knuts from ST(2) into ST (lost by FPREM)
FPREM           ; get remainder
FDIV   ST,ST(2) ; divide remainder to get number of Sickles
FISTP  S        ; store Sickles to S
FDIVR  ST,ST(2) ; divide to get number of Galleons
FISTP  G        ; store Galleons to G
FXCH            ; swap ST, ST(1) for FPREM
FPREM           ; get remainder to get number of Knuts
FISTP  K        ; store Knuts to K
ENDM

Implemented as a MACRO (basically a function), this is non-OS-specific machine-code using only the Intel 80x87 FPU / math co-processor for calculation.

Example test program with output:

FINIT           ; reset FPU

WIZ_INT_CALC    G,S,K,R,T,GS,SK     ; do the "Wizardy"

MOV  AX, K      ; display Knuts
CALL OUTDEC     ; generic decimal output routine
CALL NL         ; CRLF

MOV  AX, S      ; display Sickles
CALL OUTDEC     ; generic decimal output routine
CALL NL         ; CRLF

MOV  AX, G      ; display Galleons
CALL OUTDEC     ; generic decimal output routine
CALL NL         ; CRLF

K   DW  23          ; initial Kunts
S   DW  16          ; initial Sickles
G   DW  103         ; initial Galleons
R   DD  0.0725      ; interest rate
T   DW  3           ; time (years)
GS  DW  17          ; Galleons to Sickles exchange rate
SK  DW  29          ; Sickles to Knuts exchange rate
FRD DW  177FH       ; 8087 control word to round down

Output

# Japt, 48 bytes

XÄ pY *(U*493+V*29+W)f
Uu493
[Uz493 ,Vz29 ,Vu29]

My first try at Japt, going for @Shaggy's bounty! Needless to say, this isn't very golfy :(

Try it Online!

(g#s)k r n|(x,y)<-truncate((493*g+29*s+k)*(1+r)^n)%29=(x%17,y)
(%)=divMod

Try it online!

Thanks to @Laikoni for two bytes.

The dirty tricks: the number of coins in the input is floating point (Double), while the number of coins in the output is integral (Integer). The result is a nested pair ((Galleons, Sickles), Knotts) to avoid having to flatten to a triple.

### Explanation

-- Define a binary operator # that
-- takes the number of Galleons
-- and Slivers and produces a
-- function taking the number of
-- Knots, the rate, and the
-- number of years and producing
-- the result.
(g#s) k r n
-- Calculate the initial value
-- in Knotts, calculate the
-- final value in Knotts,
-- and divide to get the number
-- of Galleons and the
-- remainder.
|(x,y)<-truncate((493*g+29*s+k)*(1+r)^n)%29
-- Calculate the number of Slivers
-- and remaining Knotts.
=(x%17,y)
(%)=divMod

Try it online!