'#####################################
'#                                   #
'#      FPM by Ashish                #
'#                                   #
'#####################################
'28 Jun, 2019
'Save the file with name "fpm.bas" and then compile to produce fpm.exe/fpm.
'and then use commandline interface for executing commands in it.
 
 
$CONSOLE:ONLY
a$ = COMMAND$(1)
_CONSOLETITLE "Factorials, Power and Multiplication"
 
IF _TRIM$(a$) = "-help" THEN
    _ECHO "--------------------------------------"
    _ECHO "*          FPM by Ashish             *"
    _ECHO "--------------------------------------"
    _ECHO ""
    _ECHO " Usage - fpm [function_name]([arguements])"
    _ECHO "List of arguements -"
    _ECHO "1. mult()"
    _ECHO "Syntax - mult(a,b) | where a,b are positive integers of any digit."
    _ECHO "Returns a*b"
    _ECHO "Example : Try fpm mult(2,7)"
    _ECHO ""
    _ECHO "2. pow()"
    _ECHO "Syntax - pow(a,b) |where a,b are positive integers of any digit."
    _ECHO "Returns a^b"
    _ECHO "Example : Try fpm pow(3,2)"
    _ECHO ""
    _ECHO "3. fact()"
    _ECHO "Syntax - fact(a) | where a is positive integer of any digit."
    _ECHO "Returns a! , that is 1*2*3*...*(a-1)*a"
    _ECHO "Example : Try fpm fact(10)"
    _ECHO "Note : These function can also be used in the place of a and b"
    SYSTEM
END IF
dummy$ = execute$(a$, 1)
SYSTEM
 
FUNCTION execute$ (a$, sts)
    $CHECKING:OFF
    a$ = _TRIM$(a$)
    IF RIGHT$(a$, 1) <> ")" AND sts = 1 THEN a$ = ""
 
    SELECT CASE LEFT$(a$, 4)
        CASE "mult"
            brc = 0
            FOR i = 1 TO LEN(a$)
                IF MID$(a$, i, 1) = "," AND brc = 1 THEN EXIT FOR
                IF MID$(a$, i, 1) = "(" THEN brc = brc + 1
                IF MID$(a$, i, 1) = ")" THEN brc = brc - 1
            NEXT
            IF sts = 1 THEN t# = TIMER
            num_a$ = _TRIM$(MID$(a$, 6, i - 6))
            num_b$ = _TRIM$(MID$(a$, i + 1, LEN(a$) - (i + 1)))
            IF sts = 1 THEN
                _ECHO mult$(execute$(num_a$, 0), execute$(num_b$, 0))
                _ECHO "Time taken : " + STR$(TIMER - t#) + " seconds"
            ELSE
                execute$ = mult$(execute$(num_a$, 0), execute$(num_b$, 0))
            END IF
        CASE "fact"
            n~&& = VAL(execute$(MID$(a$, 6, LEN(a$) - 6), 0))
            IF sts = 1 THEN
                IF n~&& = 0 OR n~&& = 1 THEN _ECHO "1": SYSTEM
                IF n~&& = 2 THEN _ECHO "2": SYSTEM
            ELSE
                IF n~&& = 0 OR n~&& = 1 THEN execute$ = "1": EXIT FUNCTION
                IF n~&& = 2 THEN execute$ = "2": EXIT FUNCTION
            END IF
            c$ = "1"
            IF sts = 1 THEN
                IF n~&& > 500 THEN _ECHO "Can take " + STR$(2 ^ ((n~&& / 500) + 1)) + " seconds or more."
                t# = TIMER
            END IF
            FOR i~&& = 2 TO n~&&
                c$ = mult(c$, _TRIM$(STR$(i~&&)))
            NEXT
            IF sts = 1 THEN
                _ECHO c$
                _ECHO "Time taken : " + STR$(TIMER - t#) + " seconds"
            ELSE execute$ = c$
            END IF
        CASE "pow("
            brc = 0
            FOR i = 1 TO LEN(a$)
                IF MID$(a$, i, 1) = "," AND brc = 1 THEN EXIT FOR
                IF MID$(a$, i, 1) = "(" THEN brc = brc + 1
                IF MID$(a$, i, 1) = ")" THEN brc = brc - 1
            NEXT
            b$ = execute$(_TRIM$(MID$(a$, 5, i - 5)), 0)
            c$ = execute$(_TRIM$(MID$(a$, i + 1, LEN(a$) - (i + 1))), 0)
        
            c~&& = VAL(c$)
            IF sts = 1 THEN
                IF VAL(b$) = 0 AND c~&& = 0 THEN _ECHO "not defined.": SYSTEM
                IF c~&& = 0 THEN _ECHO "1": SYSTEM
                IF c~&& = 1 THEN _ECHO b$: SYSTEM
            ELSE
                IF VAL(b$) = 0 AND c~&& = 0 THEN _ECHO "not defined.": SYSTEM
                IF c~&& = 0 THEN execute$ = "1": EXIT FUNCTION
                IF c~&& = 1 THEN execute$ = b$: EXIT FUNCTION
            END IF
            d$ = b$
 
            IF sts = 1 THEN t# = TIMER
            FOR i~&& = 1 TO c~&& - 1
                d$ = mult(d$, b$)
            NEXT
            IF sts = 1 THEN
                _ECHO d$
                _ECHO "Time taken : " + STR$((TIMER - t#)) + " seconds"
            ELSE
                execute$ = d$
            END IF
        CASE ELSE
            IF sts = 1 THEN
                _ECHO ""
                _ECHO "Syntax error."
                _ECHO "Try fpm -help"
            ELSE execute$ = a$
            END IF
    END SELECT
    $CHECKING:ON
END FUNCTION
 
FUNCTION mult$ (n1$, n2$)
    $CHECKING:OFF
    IF VAL(n1$) = 0 OR VAL(n2$) = 0 THEN mult$ = "0": EXIT FUNCTION
    IF LEN(n1$) = 1 THEN mult$ = mult_single_dgt$(n2$, n1$): EXIT FUNCTION
    IF LEN(n2$) = 1 THEN mult$ = mult_single_dgt$(n1$, n2$): EXIT FUNCTION
    DIM res$(LEN(n2$))
    FOR i = LEN(n2$) TO 1 STEP -1
        dgt$1 = MID$(n2$, i, 1)
        res$(LEN(n2$) - i) = mult_single_dgt(n1$, dgt$1) + STRING$(LEN(n2$) - i, "0")
        p1 = LEN(res$(LEN(n2$) - i))
        IF p1 > p0 THEN g = p1
        p0 = p1
    NEXT
    FOR i = 0 TO LEN(n2$) - 1
        IF LEN(res$(i)) < g THEN res$(i) = STRING$(g - LEN(res$(i)), "0") + res$(i)
        ' _echo res$(i)
    NEXT
    FOR i = g TO 1 STEP -1
        sum = 0
        FOR j = 0 TO UBOUND(res$) - 1
            sum = sum + VAL(MID$(res$(j), i, 1))
        NEXT
        ' ? sum
        str_sum$ = _TRIM$(STR$(sum + carry))
        IF i > 1 THEN
            IF LEN(str_sum$) > 1 THEN
                final_dgt$ = RIGHT$(str_sum$, 1)
                carry = VAL(LEFT$(str_sum$, LEN(str_sum$) - 1))
            ELSE
                final_dgt$ = str_sum$
                carry = 0
            END IF
        ELSE
            final_dgt$ = str_sum$
            IF LEN(final_dgt$) > 1 THEN
                temp$ = ""
                FOR j = LEN(final_dgt$) TO 1 STEP -1
                    temp$ = temp$ + MID$(final_dgt$, j, 1)
                NEXT
                final_dgt$ = temp$
            END IF
        END IF
        final_sum$ = final_sum$ + final_dgt$
    NEXT
    FOR i = LEN(final_sum$) TO 1 STEP -1
        mult$ = mult$ + MID$(final_sum$, i, 1)
    NEXT
    $CHECKING:ON
END FUNCTION
 
FUNCTION mult_single_dgt$ (n$, d$)
    $CHECKING:OFF
    IF d$ = "0" THEN mult_single_dgt$ = "0": EXIT FUNCTION
    IF d$ = "1" THEN mult_single_dgt$ = n$: EXIT FUNCTION
    IF n$ = "1" THEN mult_single_dgt$ = d$: EXIT FUNCTION
    FOR j = LEN(n$) TO 1 STEP -1
        dgt_1$1 = MID$(n$, j, 1)
        r = (VAL(dgt_1$1) * VAL(d$)) + carry
        ' ? r
        IF j > 1 THEN
            FOR k = 90 TO 0 STEP -10
                IF r >= k THEN carry = k * 0.1: final$ = _TRIM$(STR$(r - k)): EXIT FOR
            NEXT
        ELSE
            final$ = _TRIM$(STR$(r))
            IF LEN(n$) > 1 THEN
                tmp$ = ""
                FOR k = LEN(final$) TO 1 STEP -1
                    tmp$ = tmp$ + MID$(final$, k, 1)
                NEXT
                final$ = tmp$
            END IF
        END IF
        product$ = product$ + final$
    NEXT
    IF LEN(n$) > 1 THEN
        FOR j = LEN(product$) TO 1 STEP -1
            mult_single_dgt$ = mult_single_dgt$ + MID$(product$, j, 1)
        NEXT
    ELSE mult_single_dgt$ = product$
    END IF
    $CHECKING:ON
END FUNCTION
 