This project demonstrates how to design and write a program in Till that will implement project requirements and logic.
Project objectives:
- Arrangement of code by sections: UI, logic, state management
- Project running modes: RESTART and RESUME
- State report and validity check
- Store and retrieve project state locally
- Make the calculation process in chunks that are growing exponentially
- Each calculation state is saved and then retrieved on next run
- State includes: iterations number, calculated π, delta and duration
- Save calculation results in log file
Note:
Though there is a thorough commentating in the code, full analysis and explanation of the code is beyond the scope of this overview, and the developer is advised to see all programming guidance and system functionalities in Till Programming Reference.
Project’s code:
! PI CALCULATOR PROJECT
! ───────────────────────── OVERVIEW ───────────────────────────────────────
! Calculating π by summation of alternating signed reciprocals, is
! an ongoing process. The more times it is run, the more accurate
! is the calculation. Since on each iteration we raise the
! iterations number by the power of ten, the calculation time can
! be longer as well. All project state is saved by system storage
! module, and can be retrieved by any Till program with the same
! program name. The results are saved also in a log file.
! ━━━━━━━━━━━━━━━━━━━━━━━━━ HEADER ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! -- variables declarations and initial actions section --
! this section is executed on program run
! -- user specified data --
! restart - restart calculation process (set FALSE after first run!)
RESTART \f
! report - report state
REPORT \t
! validate storage - handle storage validity
VALIDATESTORAGE \f
! -- incoming arguments mgmt --
! normally the program will be converted to an executable
! program 'pi-calculator.exe', and run in the background with
! a parameter. RESTART flag will be read from system args.
!
!
! to make an executable program, run:
! > till pi-calculator x
!
! to restart, run:
! > pi-calculator 1
! to disable restart, run:
! > pi-calculator 0
! to run without affecting RESTART flag, run:
! > pi-calculator
!
! system args are read from system variable 'in' (record), and
! their existence can be checked:
hasin (len in ? \t \ \f)
hasin ? RESTART (in 0 ? \t \ \f)
! -- global variables --
! symbols replacement and constants
N newline
T \t
F \f
PI math.pi
! storage
statekeys 'power' 'denominator' 'sum' 'iternums' 'calcpis' 'deltas' 'durs'
checkkey statekeys 0
! io
logpath appname + '.result'
copylogpath appname + '.copy.result'
! logic
power sum 0
denominator 1
iternums calcpis deltas durs
time { tick 0 fulldate '' }
! ━━━━━━━━━━━━━━━━━━━━━━━━━ FUNCTIONS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
! -- functions section --
! first function is executed on program run, provided
! that it does not contain any parameter
! ───────────────────────── MAIN ───────────────────────────────────────────
! program's initial function (name it as you like...)
go:
not init ? %
~
calculate
setstate
^
! ───────────────────────── INIT ───────────────────────────────────────────
! usually a function that is run only once
! to initialize program state and data
init:
msg ''
msg ++ 'Calculating π by summation of alternating ' + N
msg ++ 'signed reciprocals. All process data is' + N
msg ++ 'saved in file: `' + logpath + '`' + N
print msg
pause 3.3
clear
RESTART ?
print 'Restarting calculation process...' + N
dropstate
io.exists logpath ?
io.write copylogpath
io.copy logpath copylogpath
io.write logpath
^
not hasin ?
pause 0.7
print 'Set RESTART flag to FALSE, and run again!'
%% F
^
^
setstate RESTART
VALIDATESTORAGE ? %% validatestate \ getstate
nostate not storage.exists checkkey
REPORT ? nostate ? print 'state not exists' \ showstate
not RESTART ? print 'Resuming calculation process...'
pause 2.2
clear
print 'Process is running...' + N + N
T
! ───────────────────────── STATE MGMT ─────────────────────────────────────
! optional function to validate state and
! alert if error
! returns: storage validity
validatestate:
i 2 ~
statesexist ?
getstate
%% T
\
i 0 ?
storage.clear
setstate
pause 1
\
print 'storage error!'
pause 2.2
exit
^
^
^
F
! check existence of all storage keys
statesexist:
statekeys k ~ not storage.exists k ? %% F
T
! get state from storage
getstate:
power storage.get 'power'
denominator storage.get 'denominator'
sum storage.get 'sum'
iternums storage.get 'iternums'
calcpis storage.get 'calcpis'
deltas storage.get 'deltas'
durs storage.get 'durs'
! store state in storage
!
! params:
! anyway - state setting mode
! - true: overwrite
! - false: only if not exists
setstate anyway:
!if anyway parameter is omitted, enforce setting
null anyway ? anyway T
storage.set 'power' power anyway
storage.set 'denominator' denominator anyway
storage.set 'sum' sum anyway
storage.set 'iternums' iternums anyway
storage.set 'calcpis' calcpis anyway
storage.set 'deltas' deltas anyway
storage.set 'durs' durs anyway
! remove all application state from storage
dropstate: statekeys k ~ storage.unset k
! ───────────────────────── LOGIC ──────────────────────────────────────────
! calaulate pi from last summation by summation
! of alternating signed reciprocals of odd numbers
calculate:
time now
sec.start time.tick
signum 1
delta 0
! math.floor converts a floating point variable into an integer
iternum math.floor math.power 10 power + 1
lastiternum math.floor math.power 10 power
max iternum - lastiternum
max < 10 ? max 10
index max ~
sum ++ signum / denominator
signum ** -1
denominator ++ 2
^
power ++
calcpi 4 * sum
delta PI - calcpi
time now
sec.end time.tick
! duration in seconds
dur (sec.end - sec.start) / 1000
iternums <- iternum
calcpis <- calcpi
deltas <- delta
durs <- dur
msg ''
msg ++ time.fulldate + N
msg ++ '‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾' + N
msg ++ 'iterations ' + iternum + N
msg ++ 'calculated pi ' + calcpi + N
msg ++ 'delta ' + delta + N
msg ++ 'duration ' + dur + N + N + N
REPORT ? print msg
io.append logpath msg
! ───────────────────────── UI ─────────────────────────────────────────────
! show storage data
showstate:
itemsnum len iternums
msg 'state' + N
msg ++ '‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾' + N
msg ++ 'power ' + power + N
msg ++ 'denominator ' + denominator + N
msg ++ 'sum ' + sum + N
not itemsnum ?
msg ++ 'state is empty'
\
ind math.abs 1 - itemsnum
msg ++ N + 'iterations ' + iternums ind
msg ++ N + 'calcilated pi: ' + calcpis ind
msg ++ N + 'delta: ' + deltas ind
lastdur durs ind
msg ++ N + 'duration: ' + lastdur
! estimation of next calculation duration
nextdur 10 * lastdur
nextdurstr nextdur
ind string.index nextdur '.'
ind ?
slen len nextdur
prefind ind + 4
prefind > slen ? prefind slen
nextdurstr string.section nextdur 0 prefind
^
msg ++ N + N + 'estimated next duration: ' + nextdurstr + ' seconds'
^
msg ++ N + N
print msg
print 'Press enter to run'
input