Price list behind API: Difference between revisions
Content added Content deleted
Thundergnat (talk | contribs) m (syntax highlighting fixup automation) |
|||
Line 202: | Line 202: | ||
From 90121 to 95102 with 4998 items |
From 90121 to 95102 with 4998 items |
||
From 95103 to 99999 with 4809 items |
From 95103 to 99999 with 4809 items |
||
</pre> |
|||
=={{header|jq}}== |
|||
'''Works with jq and gojq, the C and Go implementations of jq''' |
|||
In this entry, the API is envisioned as the interface between a server and a client, |
|||
where the server is modeled by a database together with a program that reads the database and responds to client commands; |
|||
and the client is modeled as an indefinitely long stream of API calls of the form specified in the task description. |
|||
Our server will respond to both well-formed and ill-formed API calls with JSON messages. |
|||
Since jq does not itself include a built-in PRNG, we will model the database as a file of prices generated by a jq program that uses /dev/urandom as a source of entropy, and that is separate from a second jq program (server.jq) that reads the data (once) and responds to API calls. |
|||
The driver program (server.jq) is loosely adapted from the [[#Wren|Wren]] entry. It is left as an exercise to sort the prices for more efficient computation of |
|||
the get_prange_count() API calls. |
|||
===The Server (server.sh)=== |
|||
<pre> |
|||
#!/bin/bash |
|||
ApproxNumPrices=100000 |
|||
maxPrice=100000 |
|||
JQ=jq |
|||
function prices { |
|||
< /dev/urandom tr -cd '0-9' | fold -w 1 | |
|||
$JQ -Rrnc --argjson ApproxNumPrices $ApproxNumPrices --argjson maxPrice $maxPrice ' |
|||
# Output: a PRN in range(0;$n) where $n is `.` |
|||
def prn: |
|||
if . == 1 then 0 |
|||
else . as $n |
|||
| ([1, (($n-1)|tostring|length)]|max) as $w |
|||
| [limit($w; inputs)] | join("") | tonumber |
|||
| if . < $n then . else ($n | prn) end |
|||
end; |
|||
# Output: PRN in range($m;$n) if $n > $m |
|||
def rand($m; $n): |
|||
if $m > $n then "rand\($m);\($n))" | error |
|||
else (($n-$m)|prn) + $m |
|||
end ; |
|||
range(0; $ApproxNumPrices - 50 + rand(0; 100)) |
|||
| rand(0; 1+$maxPrice)' |
|||
} |
|||
# API commands: |
|||
# get_max_price() |
|||
# get_prange_count(pricemin, pricemax) |
|||
jq -nrcR --slurpfile prices <(prices) -f server.jq |
|||
</pre> |
|||
===The Server Program (server.jq)=== |
|||
<syntaxhighlight lang=jq> |
|||
def count(s): reduce s as $_ (0; .+1); |
|||
def minDelta: 1; |
|||
def getMaxPrice: $prices | max; |
|||
def getPrangeCount($min; $max): |
|||
count( $prices[] | select($min <= . and . <= $max) ) ; |
|||
def get5000($prices; $min; $max; $n): |
|||
{ count: getPrangeCount($min; $max), |
|||
delta: (($max - $min) / 2), |
|||
$max } |
|||
| until(.count == $n or .delta < minDelta/2; |
|||
.max = (( if .count > $n then .max - .delta else .max + .delta end)|floor) |
|||
| .count = getPrangeCount($min; .max) |
|||
| .delta /= 2 ) |
|||
| [.max, .count] ; |
|||
def getAll5000($min; $max; $n): |
|||
{ mc: get5000($prices; $min; $max; $n)} |
|||
| .pmax = .mc[0] |
|||
| .pcount = .mc[1] |
|||
| .res = [[$min, .pmax, .pcount]] |
|||
| until(.pmax >= $max; |
|||
.pmin = .pmax + 1 |
|||
| .mc = get5000($prices; .pmin; $max; $n) |
|||
| .pmax = .mc[0] |
|||
| .pcount = .mc[1] |
|||
| if .pcount == 0 then "Price list from \(.pmin) has too many with same price." | error |
|||
else .res += [[.pmin, .pmax, .pcount]] |
|||
end ) |
|||
| .res ; |
|||
def trim: sub("^ +";"") | sub(" +$";""); |
|||
def display($res; $numPrices; $actualMax; $approxBinSize): |
|||
"Using \($numPrices) items with prices from 0 to \($actualMax):", |
|||
"Split into \($res|length) bins of approx \($approxBinSize) elements:", |
|||
($res[] as [$min, $max, $n] |
|||
| (if $max > $actualMax then $actualMax else $max end) as $mx |
|||
| " From \($min) to \($mx) with \($n) items" ) ; |
|||
def repl(approxBinSize): |
|||
($prices|length) as $numPrices |
|||
| getMaxPrice as $actualMax |
|||
| getAll5000(0; $actualMax; approxBinSize) as $res |
|||
| def r: |
|||
trim |
|||
| . as $line |
|||
| if startswith("get_max_price") then {getMaxPrice: getMaxPrice} |
|||
else (scan(" *get_prange_count *[(]([0-9]+) *, *([0-9]+) *[)]") |
|||
| map(tonumber) as [$min, $max] |
|||
| {"getPrangeCount": {$min, $max, count: getPrangeCount($min; $max)}}) |
|||
// {error: $line} |
|||
end; |
|||
display($res; $numPrices; $actualMax; approxBinSize), |
|||
(inputs | r); |
|||
# The approximate bin size |
|||
repl(5000) |
|||
</syntaxhighlight> |
|||
===Sample Client Calls (api.txt)=== |
|||
<pre> |
|||
get_max_price() |
|||
junk |
|||
get_prange_count(1000, 2000) |
|||
</pre> |
|||
{{output}} |
|||
Invocation: server.sh < api.txt |
|||
<pre> |
|||
Using 99989 items with prices from 0 to 99999: |
|||
Split into 21 bins of approx 5000 elements: |
|||
From 0 to 4996 with 5000 items |
|||
From 4997 to 9993 with 4999 items |
|||
From 9994 to 15031 with 4996 items |
|||
From 15032 to 20063 with 4999 items |
|||
From 20064 to 24992 with 5000 items |
|||
From 24993 to 30001 with 5000 items |
|||
From 30002 to 35032 with 4999 items |
|||
From 35033 to 40131 with 5000 items |
|||
From 40132 to 44960 with 4998 items |
|||
From 44961 to 49951 with 4998 items |
|||
From 49952 to 54930 with 4997 items |
|||
From 54931 to 59996 with 4998 items |
|||
From 59997 to 65055 with 5000 items |
|||
From 65056 to 69923 with 5000 items |
|||
From 69924 to 74834 with 4997 items |
|||
From 74835 to 79779 with 4998 items |
|||
From 79780 to 84865 with 5000 items |
|||
From 84866 to 89811 with 5000 items |
|||
From 89812 to 94818 with 5000 items |
|||
From 94819 to 99985 with 4998 items |
|||
From 99986 to 99999 with 12 items |
|||
{"getMaxPrice":99999} |
|||
{"getPrangeCount":{"min":0,"max":10,"count":17}} |
|||
{"error":"junk"} |
|||
{"getPrangeCount":{"min":1000,"max":2000,"count":1006}} |
|||
</pre> |
</pre> |
||