19
\$\begingroup\$

Given a string with a multiple people's investment data, find out how much profit/loss they recorded.

The string only contains capital and lowercase letters, like this:

AABaBbba

Each letter represents a person - a capital letter means buy, a lowercase letter means sell. The price of the stock they are investing in (CGLF) starts at $50. After someone buys, the price goes up 5%. After someone sells the price goes down 5%. You need to figure out how much money each person who participated made/lost.

Notes:

  • The string will always be valid, no selling without first buying. Also, everyone who buys a stock will sell it eventually.
  • Your calculations should be accurate to at least 6 decimal places. However, final answers should be rounded to two decimals.

Test Cases:

Input: AABaBbba

  • A: Buy - $50
  • A: Buy - $52.50
  • B: Buy - $55.125
  • a: Sell - $57.88125
  • B: Buy - $54.9871875
  • b: Sell - $57.736546875
  • b: Sell - $54.8497195313
  • a: Sell - $52.1072335547
  • Person A profit: 57.88125+52.1072335547-50-52.50=7.4884835547
  • Person B profit: 57.736546875+54.8497195313-55.125-54.9871875=2.4740789063

Output: A:7.49,B:2.47 (order doesn't matter, separators not required)


Input: DGdg

  • D: Buy - $50
  • G: Buy - $52.50
  • d: Sell - $55.125
  • g: Sell - $52.36875
  • Person D profit: 55.125-50=5.125
  • Person G profit: 52.36875-52.50=-0.13125

Output: D:5.13,G:-.13


Input: ADJdja

  • A: Buy - $50
  • D: Buy - $52.50
  • J: Buy - $55.125
  • d: Sell - $57.88125
  • j: Sell - $54.9871875
  • a: Sell - $52.237828125
  • Person A profit: 52.237828125-50=2.237828125
  • Person D profit: 57.88125-52.50=5.38125
  • Person J profit: 54.9871875-55.125=-0.1378125

Output: A:2.24,D:5.38,J:-.14

\$\endgroup\$
6
  • \$\begingroup\$ @ETHproductions When you print output, it doesn't really matter what the datatype is, it just needs to convey the right information. \$\endgroup\$
    – geokavel
    Jan 12, 2016 at 20:50
  • \$\begingroup\$ Can the final result be truncated instead of rounded? \$\endgroup\$
    – Mwr247
    Jan 12, 2016 at 22:08
  • \$\begingroup\$ @Mwr Sorry, you have to round. \$\endgroup\$
    – geokavel
    Jan 12, 2016 at 22:11
  • \$\begingroup\$ Do we have to output the people in alphabetical order? \$\endgroup\$ Jan 13, 2016 at 10:32
  • \$\begingroup\$ Also, Python rounds stuff weirdly; 5.125 rounds to 5.12 by default. Is this accepted? \$\endgroup\$ Jan 13, 2016 at 10:45

9 Answers 9

5
+100
\$\begingroup\$

APL (Dyalog Extended), 34 32 bytes

⌊{⍺,2⍕+/-⍵}⌸××50ׯ1×\⍤↓1,1+.05××
Monadic × in Dyalog extended for letters corresponds to the casing
¯1 1 ≡ ×'aA'

1+.05×× - 1 + 0.05 times the sign (either 1.05 or 0.95)
1,      - concat 1 to the front
¯1×\⍤↓  - cumulative product and drop the last element
50×     - multiply by 50
××50... - the sign times that
⌊{⍺,2⍕+/-⍵}⌸ - group (dyadic ⌸) by the lowercased input (⌊)
 {⍺,2⍕+/-⍵}  - return the left item (the person) concat their rounded total

-2 thanks to Adám

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ ¯1 1 ≡ ×'aA' and being able to floor letters makes dyalog much more elegant than J in this case. \$\endgroup\$
    – Jonah
    Jan 10, 2021 at 18:40
  • \$\begingroup\$ @Jonah only dyalog extended, it wouldn't be as nice in regular dyalog \$\endgroup\$
    – rak1507
    Jan 10, 2021 at 18:54
  • \$\begingroup\$ ×∘(×\)¯1↓ׯ1×\⍤↓ \$\endgroup\$
    – Adám
    Feb 18, 2021 at 6:50
3
\$\begingroup\$

Java, 277 bytes

class c{public static void main(String[]a){double[]m=new double[26];double s=50;for(byte b:a[0].getBytes()){if(b>=97){m[b-97]+=s;s*=.95;}else{m[b-65]-=s;s*=1.05;}}char g=65;for(double k:m){if(k!=0){System.out.println(g+String.format(java.util.Locale.ENGLISH,"%.2f",k));}g++;}}}

Ungolfed:

class c {
    public static void main(String[]a) {
        double[] m = new double[26];
        double s = 50;
        for(byte b : a[0].getBytes()) {
            if(b>=97) {
                m[b-97]+=s;  
                s*=.95;
            } else {
                m[b-65]-=s;
                s*=1.05;
            }
        }
        char g=65;
        for(double k:m) {
            if(k!=0) {
                System.out.println(g+String.format(java.util.Locale.ENGLISH,"%.2f",k));
            }
            g++;
        }
    }
}
\$\endgroup\$
8
  • \$\begingroup\$ A lot of the formatting is unnecessary. Having a 0 before the decimal point is o.k., commas and colons are not necessary. \$\endgroup\$
    – geokavel
    Jan 12, 2016 at 20:47
  • \$\begingroup\$ great this reduces my byte count significantly \$\endgroup\$
    – ByteBit
    Jan 12, 2016 at 20:49
  • \$\begingroup\$ The class doesn't need to be public. \$\endgroup\$ Jan 12, 2016 at 21:43
  • \$\begingroup\$ Is specifying the Locale necessary? I don't mind if it shows up as "3,54". \$\endgroup\$
    – geokavel
    Jan 13, 2016 at 16:39
  • \$\begingroup\$ Store your money in a float, this saves bytes and might get you some extra profit ;) \$\endgroup\$ Jan 13, 2016 at 16:54
2
\$\begingroup\$

JavaScript (ES7), 145 142 bytes

I can't find a shorter way to round out the results...

x=>[for(c of(i=50,a={},x))(a[d=c.toUpperCase()]=a[d]||0,c<"["?(a[d]-=i,i*=1.05):(a[d]+=i,i*=.95))]&&Object.keys(a).map(k=>[k,a[k].toFixed(2)])

Fun fact: this would only be 101 bytes if not for the rounding requirement:

x=>[for(c of(i=50,a={},x))(a[d=c.toUpperCase()]=a[d]||0,c<"["?(a[d]-=i,i*=1.05):(a[d]+=i,i*=.95))]&&a
\$\endgroup\$
1
  • \$\begingroup\$ 43 bytes seems to much..I'm sure you can find a better way! \$\endgroup\$
    – geokavel
    Jan 12, 2016 at 21:08
2
\$\begingroup\$

Python 3, 116 bytes

P=50
M={}
for c in input():C=c.upper();u=c>C;u+=~-u;M[C]=M.get(C,0)+P*u;P*=1-u*.05
for k in M:print(k,round(M[k],2))

Ungolfed

price = 50
profits = {}
for char in input():
    upper = char.upper()
    direction = 2 * (char > upper) - 1
    profits[upper] = profits.get(upper, 0) + price * direction
    price *= 1 - direction * .05
for key in profits:
    print(key, round(profits[key], 2))
\$\endgroup\$
2
  • \$\begingroup\$ It seems shorter to just do u=2*(c>C)-1 directly. \$\endgroup\$
    – xnor
    Jan 13, 2016 at 21:11
  • \$\begingroup\$ at the end of line 3, /20 would save one byte over *.05 \$\endgroup\$
    – movatica
    Feb 18, 2021 at 13:33
2
\$\begingroup\$

ARM Thumb-2 (VFP + printf, softfp ABI), 109 104 bytes

Uses the softfp ABI (default on Android). It won't work on armhf Linux. f7ff fffe is a 4 byte linker placeholder for printf.

Both of these examples must be placed at an address that is not a multiple of 4 (but even), and must be in a W+X section because we modify an inline printf string in place.

b5f8 2100 2234 3a01 b402 d8fc f2c4 0249
ec42 1b10 ed9f 1b11 f810 2b01 b192 3a61
bf48 3220 eb0d 02c2 ed92 5b00 bf47 ee35
5b40 ee00 0b01 ee35 5b00 ee00 0b41 ed82
5b00 e7e9 2641 bc0c b11b a005 7006 f7ff
fffe 3601 2e5a ddf6 bdf8 999a 9999 9999
3fa9 2558 322e 0066

Assembly:

        .syntax unified
        .arch armv6t2
        // We need VFPv2 for this.
        .fpu vfpv2
        // We modify an inline string in place, so we need W+X.
        .section ".writeable_text","awx",%progbits
        .thumb
        .globl silly_stocks
        .thumb_func
        // Must NOT be 4 byte aligned.
        // .align 4
        // nop

        // void silly_stocks(const char *input);
        // Follows AAPCS convention.
        // input is a null-terminated string in r0.
silly_stocks:
        push          {r3-r7, lr}
        // double buf[26] = {0}
        movs          r1, #0
        movs          r2, #26*2
.Lclear_loop:
        subs          r2, #1
        push          {r1}
        bhi           .Lclear_loop
.Lclear_loop_end:
        // note: r1 and r2 are zero at this point
        // r1:r2 = 0x4049000000000000 = 50.0 double
        movt           r2, #0x4049
        vmov           d0, r1, r2
        vldr           d1, .Lfloat_pool
.Lprocess_loop:
        // Loop until the null terminator.
        // while (r2 = *input++)
        ldrb          r2, [r0], #1
        cbz           r2, .Lprocess_loop_end
        // tolower(r2) - 'a'
        //
        // Specifically, we subtract 'a', and if the char was uppercase,
        // it would be negative, triggering the N flag, which is why we
        // check the mi condition in the following IT blocks.
        subs          r2, #'a'
        it            mi
        // if (isupper(r2))
          addmi       r2, #'a' - 'A'

        // Yucky. We can't use proper offsetting with VFP and we need to
        // keep the flags for the IT block.
        add.w         r2, sp, r2, lsl #3
        // Behold, the beautiful syntax of VFP/NEON.
        // Combine it with a an IT block and you get a 10 letter mnemonic. OwO
        vldr.64       d5, [r2]
        ittee         mi
        // if (isupper(r2)) {    // Buy
          vsubmi.f64  d5, d5, d0 //   profit[i]   -= d0
          vmlami.f64  d0, d0, d1 //   stock_price += stock_price * 0.05
        // } else {              // Sell
          vaddpl.f64  d5, d5, d0 //   profit[i]   += stock_price
          vmlspl.f64  d0, d0, d1 //   stock_price -= stock_price * 0.05
        // } endif
        vstr.64       d5, [r2]
        b             .Lprocess_loop
.Lprocess_loop_end:

        movs          r6, #'A'
.Lprint_loop:
        // Pop the double into r2 and r3. We are using the softfp abi, so
        // doubles are passed in integer registers.
        pop           {r2, r3}
        // Only check if the most signicant half of the double is zero.
        // Any non-zero double that is 0x00000000xxxxxxxx is denormal.
        cbz           r3, .Lprint_loop_skip
        // Set up an AEABI call to printf.
        adr           r0, .Lprintf_str
        // Instead of %c, store the char directly (that's why we need W+X)
        strb          r6, [r0]
        bl            printf
.Lprint_loop_skip:
        // Loop from A-Z.
        adds          r6, #1
        cmp           r6, #'Z'
        ble           .Lprint_loop
        pop           {r3-r7, pc}
        // At least for the softfp ABI, doubles are smaller than floats.
        // This is because printf takes doubles, not floats, and we need
        // to pass them in a register pair.
        // So we would need something like this, which is 12 bytes.
        //  vpop         {d0}
        //  vcvt.f64.f32 d0, s0
        //  vmov         r2, r3, d0
        .align 2
.Lfloat_pool:
        .double       0.05
.Lprintf_str:
        // Our printf string. We modify X directly.
        .asciz        "X%.2f"

        .section ".testsuite_unscored","ax",%progbits
.macro $TEST str
        adr           r0, 1f
        bl            puts
        adr           r0, 1f
        bl            silly_stocks
        movs          r0, #'\n'
        bl            putchar
        b             2f
        .align 2
1:
        .asciz        "\str"
.align 2
2:
.endm

        .align 2
        .globl main
        .thumb_func
main:
        push          {r4, lr}
        $TEST         "AABaBbba"
        $TEST         "DGdg"
        $TEST         "ADJdja"
        pop           {r4, pc}

Decided to take a shot at VFP code in ARM assembly instead of doing something easy (and basically identical to the Java entry) like C or C++.

One of the things I realized the hard way was that ARM actually needs the stack to be 8 byte aligned, or else subtle bugs will occur. Two hours of debugging I am NEVER getting back. 😫

Oddly enough, using doubles is smaller than floats, despite the larger literal pool. This is because the C calling convention mandates that floats be promoted to double in variadic functions, so we would have to convert to double anyways, and that is larger than the 8 bytes for the larger pool.

Output isn't pretty. But the question says "separators not required" so I took that in the most literal sense. 😏

AABaBbba
A7.49B2.47
DGdg
D5.12G-0.13
ADJdja
A2.24D5.38J-0.14

But aside from the 5.12 which is the same rounding error that was mentioned in Python, the results are correct.

ARM Thumb-2 (VFP + printf, softfp ABI, very imprecise), 99 96 bytes

This version uses 32-bit x 16-bit fixed point arithmetic.

It lacks the required 6 digits of precision, but for the test cases it products the same results. Interpret it as you wish.

Must be loaded at an address that is not a multiple of 4 for constant pool alignment.

Machine code:

b5f8 2100 2534 3d01 b402 d8fc f2c0 0132
4d11 f810 2b01 b172 3a61 bf48 3220 f85d
3032 fb51 f605 bf47 1a5b 4431 440b 1b89
f84d 3032 e7ed 2641 ecbd 0b02 eeba 0bc8
ec53 2b10 b11b a004 7006 f7ff fffe 3601
2e5a ddf1 bdf8 cccc 0ccc 2558 322e 0066

Assembly:

        .syntax unified
        .arch armv6t2
        // We need VFPv2 for this.
        .fpu vfpv2
        // We modify an inline string in place, so we need W+X.
        .section ".writeable_text_fixed","awx",%progbits
        .thumb
        .globl silly_stocks_fixed
        .thumb_func

        // Must not be 4 byte aligned.
        // .align 4
        // nop
        // void silly_stocks_fixed(const char *input);
        // Follows AAPCS convention.
        // input is a null-terminated string in r0.
silly_stocks_fixed:
        push          {r3-r7, lr}
        // We use double the width because we need to keep alignment.
        // int64_t buf[26] = {0}
        movs          r1, #0
        movs          r5, #26*2
.Lclear_loop_fixed:
        subs          r5, #1
        push          {r1}
        bhi           .Lclear_loop_fixed
.Lclear_loop_end_fixed:
        // Load our float constants from the pool.
        movt          r1, #0x32       // 0x00320000 = 50.0 in 16-bit fixed point
        ldr           r5, .L.05.fix32 // 0x0ccccccc = 0.05 in 32-bit fixed point
.Lprocess_loop_fixed:
        // Loop until the null terminator.
        // while (r2 = *input++)
        ldrb          r2, [r0], #1
        cbz           r2, .Lprocess_loop_end_fixed
        // tolower(r2) - 'a'
        //
        // Specifically, we subtract 'a', and if the char was uppercase,
        // it would be negative, triggering the N flag, which is why we
        // check the mi condition in the following IT blocks.
        subs          r2, #'a'
        it            mi
        // if (isupper(r2))
          addmi       r2, #'a' - 'A'

        ldr           r3, [sp, r2, lsl #3]
        // fixed point multiply:
        // x<fixA> = (y<fixB> * z<fixC>) >> (B + C - A)
        // tmp<fix16> = (stock_price<fix16> * 0.05<fix32>) >> 32
        smmul         r6, r1, r5  // r6 = stock_price * 0.05
        ittee         mi
        // if (isupper(r2)) {     // Buy
          submi       r3, r1      //   profit[i]   -= stock_price
          addmi       r1, r6      //   stock_price += stock_price * 0.05
        // } else {               // Sell
          addpl       r3, r1      //   profit[i]   += stock_price
          subpl       r1, r6      //   stock_price -= stock_price * 0.05
        // } endif
        str           r3, [sp, r2, lsl #3]
        b             .Lprocess_loop_fixed
.Lprocess_loop_end_fixed:
        movs          r6, #'A'
.Lprint_loop_fixed:
        // Pop the result into d0 (also cleaning the stack)
        vpop          {d0}
        // Convert from fix16 to double
        vcvt.f64.s32  d0, d0, #16
        // Move the double result into r2 and r3. This is for the softfp ABI
        // which passes floating point values in registers.
        vmov          r2, r3, d0
        // Only check if the most signicant half of the double is zero.
        // Any non-zero double that is 0x00000000xxxxxxxx is denormal.
        cbz           r3, .Lprint_loop_skip_fixed
        // Set up an AEABI call to printf.
        adr           r0, .Lprintf_str_fixed
        // Instead of %c, store the char directly (that's why we need W+X)
        strb          r6, [r0]
        bl            printf
.Lprint_loop_skip_fixed:
        // Loop from A-Z.
        adds          r6, #1
        cmp           r6, #'Z'
        ble           .Lprint_loop_fixed
        pop           {r3-r7, pc}
        // 0.05 in fix32.
.L.05.fix32:
        .word         0x0ccccccc
        // Our printf string. We replace the X when we use it.
.Lprintf_str_fixed:
        .asciz        "X%.2f"

        .section ".testsuite_unscored","ax",%progbits
.macro $TEST str
        adr           r0, 1f
        bl            puts
        adr           r0, 1f
        bl            silly_stocks_fixed
        movs          r0, #'\n'
        bl            putchar
        b             2f
        .align 2
1:
        .asciz        "\str"
.align 2
2:
.endm

        .align 2
        .globl main
        .thumb_func
main:
        push          {r4, lr}
        $TEST         "AABaBbba"
        $TEST         "DGdg"
        $TEST         "ADJdja"
        pop           {r4, pc}

For the reference, here is the results for float vs fixed point at 6 digits of precision.

AABaBbba
float: A:7.488484,B:2.474079,
fixed: A:7.488434,B:2.474060,
DGdg
float: D:5.125000,G:-0.131250,
fixed: D:5.124969,G:-0.131256,
ADJdja
float: A:2.237828,D:5.381250,J:-0.137812,
fixed: A:2.237808,D:5.381226,J:-0.137817,
\$\endgroup\$
1
\$\begingroup\$

Perl 5 -MList::Util=pairmap -F, 80 bytes

$p=50;map{$k{uc$_}+=$q=95>ord?-$p:$p;$p-=$q/20}@F;pairmap{printf"$a:%.2f ",$b}%k

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Javascript (ES6, Node.js), 136 bytes

Source

s=>(m=50,a={},[...s].map(p=>(u=p.toUpperCase(),n=p==u?1:-1,a[u]=(a[u]||0)-m*n,m*=1+n/20)),Object.keys(a).map(k=>a[k]=a[k].toFixed(2)),a)

Explanation

f=s=>( // (s)tring
    m=50, // stock value (m)oney
    a={}, // (a)ll people
    [...s].map(
        p=>(
            u=p.toUpperCase(), // (u)ppercase: used to group buys and sells for each person
            n=p==u?1:-1, // sig(n): 1 for buy, -1 for sell
            a[u]=(a[u]||0)-m*n, // adjust balance with stock value and sign
            m*=1+n/20 // adjust stock value
        )
    ),
    Object.keys(a).map(k=>a[k]=a[k].toFixed(2)), // round profits
    a // return profits
)

Proof

f=s=>(m=50,a={},[...s].map(p=>(u=p.toUpperCase(),n=p==u?1:-1,a[u]=(a[u]||0)-m*n,m*=1+n/20)),Object.keys(a).map(k=>a[k]=a[k].toFixed(2)),a)
const TESTS = [
    ['AABaBbba',{A:'7.49',B:'2.47'}],
    ['DGdg',{D:'5.13',G:'-0.13'}],
    ['ADJdja',{A:'2.24',D:'5.38',J:'-0.14'}]
]

TESTS.forEach(([input,expected])=>{
    console.log(input);
    console.log(expected);
    console.log(f(input))
});

\$\endgroup\$
1
0
\$\begingroup\$

Japt, 91 84 bytes

A=[]J=50¡AhD=Xc %H(X<'_?[AgD ª0 -JJ*=1.05]:[AgD +JJ*=.95] g};A£X©[Y+I d X*L r /L]} f

Based on my JS answer. Try it online!

\$\endgroup\$
0
\$\begingroup\$

J, 64 bytes

tolower(~.@[,.(0j2":_50*+/)/.)](]*1*/\@}:@,1+0.05*])_1+2*91>3&u:

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.