# Price fraction

Price fraction
You are encouraged to solve this task according to the task description, using any language you may know.

A friend of mine runs a Pharmacy. He has a specialised rounding function in his Dispensary application which receives a decimal value of currency and forces it to a standard value. This value is regulated by a government department.

Task: Given a floating point value between 0.00 and 1.00, rescale according to the following table:

```>=  0.00  <  0.06  :=  0.10
>=  0.06  <  0.11  :=  0.18
>=  0.11  <  0.16  :=  0.26
>=  0.16  <  0.21  :=  0.32
>=  0.21  <  0.26  :=  0.38
>=  0.26  <  0.31  :=  0.44
>=  0.31  <  0.36  :=  0.50
>=  0.36  <  0.41  :=  0.54
>=  0.41  <  0.46  :=  0.58
>=  0.46  <  0.51  :=  0.62
>=  0.51  <  0.56  :=  0.66
>=  0.56  <  0.61  :=  0.70
>=  0.61  <  0.66  :=  0.74
>=  0.66  <  0.71  :=  0.78
>=  0.71  <  0.76  :=  0.82
>=  0.76  <  0.81  :=  0.86
>=  0.81  <  0.86  :=  0.90
>=  0.86  <  0.91  :=  0.94
>=  0.91  <  0.96  :=  0.98
>=  0.96  <  1.01  :=  1.00
```

## AutoHotkey

<lang AutoHotkey>; Submitted by MasterFocus --- http://tiny.cc/iTunis

Loop {

``` InputBox, OutputVar, Price Fraction Example, Insert the value to be rounded.`n* [ 0 < value < 1 ]`n* Press ESC or Cancel to exit, , 200, 150
If ErrorLevel
Break
MsgBox % "Input: " OutputVar "`nResult: " PriceFraction( OutputVar )
```

}

-----------------------------------------

PriceFraction( p_Input ) {

``` If p_Input is not float ; returns 0 if input is not a float
Return 0
```
``` If ( ( p_Input <= 0 ) OR ( p_Input >= 1 ) ) ; returns 0 is input is out of range
Return 0
```
``` ; declaring the table (arbitrary delimiters in use are '§' and '|')
l_List := "0.06|0.10§0.11|0.18§0.16|0.26§0.21|0.32§0.26|0.38§0.31|0.44§0.36|0.50§0.41|0.54§0.46|0.58§0.51|0.62§0.56|0.66§0.61|0.70§0.66|0.74§0.71|0.78§0.76|0.82§0.81|0.86§0.86|0.90§0.91|0.94§0.96|0.98§1.01|1.00"
```
``` Loop, Parse, l_List, § ; retrieves each field (delimited by '§')
{
StringSplit, l_Array, A_LoopField, | ; splits current field (using delimiter '|')
If ( p_Input <= l_Array1 )
Return l_Array2 ; returns the second value if input <= first value
}
```
``` Return 0 ; returns 0, indicating failure (shouldn't be reached though)
```

}</lang>

## ALGOL 68

Translation of: C

- note: This specimen retains the original C coding style.

Works with: ALGOL 68 version Revision 1 - no extensions to language used
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny

<lang algol68>main: (

```   # Just get a random price between 0 and 1 #
# srand(time(NIL)); #
REAL price := random;
REAL tops := 0.06;
REAL std val := 0.10;
```
```   # Conditionals are a little odd here "(price-0.001 < tops AND
price+0.001 > tops)" is to check if they are equal. Stupid
C floats, right?   :) #
WHILE ( price>tops OR (price-0.001 < tops AND price+0.001 > tops) ) AND tops<=1.01
DO
tops+:=0.05;
```
```       IF std val < 0.26 THEN
std val +:= 0.08
ELIF std val < 0.50 THEN
std val +:= 0.06
ELSE
std val +:= 0.04
FI;
```
```       IF std val > 0.98 THEN
std val := 1.0
FI
OD;
```
```   printf((\$"Value :   "z.2dl,"Converted to standard :   "z.2dl\$, price, std val))
```

)</lang> Sample Output:

```Value :   0.38
Converted to standard :   0.54
```

## BASIC

Works with: QBasic

This could also be done by building an array, but I felt that this was simpler.

<lang qbasic>DECLARE FUNCTION PriceFraction! (price AS SINGLE)

RANDOMIZE TIMER DIM x AS SINGLE x = RND PRINT x, PriceFraction(x)

FUNCTION PriceFraction! (price AS SINGLE)

```   'returns price unchanged if invalid value
SELECT CASE price
CASE IS < 0!
PriceFraction! = price
CASE IS < .06
PriceFraction! = .1
CASE IS < .11
PriceFraction! = .18
CASE IS < .16
PriceFraction! = .26
CASE IS < .21
PriceFraction! = .32
CASE IS < .26
PriceFraction! = .38
CASE IS < .31
PriceFraction! = .44
CASE IS < .36
PriceFraction! = .5
CASE IS < .41
PriceFraction! = .54
CASE IS < .46
PriceFraction! = .58
CASE IS < .51
PriceFraction! = .62
CASE IS < .56
PriceFraction! = .66
CASE IS < .61
PriceFraction! = .7
CASE IS < .66
PriceFraction! = .74
CASE IS < .71
PriceFraction! = .78
CASE IS < .76
PriceFraction! = .82
CASE IS < .81
PriceFraction! = .86
CASE IS < .86
PriceFraction! = .9
CASE IS < .91
PriceFraction! = .94
CASE IS < .96
PriceFraction! = .98
CASE IS < 1.01
PriceFraction! = 1!
CASE ELSE
PriceFraction! = price
END SELECT
```

END FUNCTION</lang>

Sample outputs (run 5 times):

```.7388727      .82
.8593103      .9
.826687       .9
.3444635      .5
.0491907      .1
```

## C

<lang c>#include <stdlib.h>

1. include <time.h>
2. include <stdio.h>

int main() { //Just get a random price between 0 and 1 srand(time(NULL)); float price = (float)((int)rand()%101)/100; float tops=0.06; float std_val = 0.10;

```   //Conditionals are a little odd here
//"(price-0.001 < tops && price+0.001 > tops)" is to check if they are equal. Stupid C floats, right? :)
while((price>tops || (price-0.001 < tops && price+0.001 > tops)) && tops<=1.01)
{
tops+=0.05;
```
```       if(std_val < 0.26)
```

std_val += 0.08; else if(std_val<0.50) std_val +=0.06; else std_val +=0.04;

if(std_val > 0.98) std_val = 1.0;

```   }
```

printf("Value: %.2f\nConverted to standard: %.2f\n", price, std_val); }</lang>

## C#

<lang csharp>namespace ConsoleApplication1 {

```   class Program
{
static void Main(string[] args)
{
for (int x = 0; x < 10; x++)
{
Console.WriteLine("In: {0:0.00}, Out: {1:0.00}", ((double)x) / 10, SpecialRound(((double)x) / 10));
}
```
```           Console.WriteLine();
```
```           for (int x = 0; x < 10; x++)
{
Console.WriteLine("In: {0:0.00}, Out: {1:0.00}", ((double)x) / 10 + 0.05, SpecialRound(((double)x) / 10 + 0.05));
}
```
```           Console.WriteLine();
Console.WriteLine("In: {0:0.00}, Out: {1:0.00}", 1.01, SpecialRound(1.01));
```
```           Console.Read();
}
```
```       private static double SpecialRound(double inValue)
{
if (inValue > 1) return 1;
```
```           double[] Splitters = new double[] {
0.00 , 0.06 , 0.11 , 0.16 , 0.21 ,
0.26 , 0.31 , 0.36 , 0.41 , 0.46 ,
0.51 , 0.56 , 0.61 , 0.66 , 0.71 ,
0.76 , 0.81 , 0.86 , 0.91 , 0.96 };
```
```           double[] replacements = new double[] {
0.10 , 0.18 , 0.26 , 0.32 , 0.38 ,
0.44 , 0.50 , 0.54 , 0.58 , 0.62 ,
0.66 , 0.70 , 0.74 , 0.78 , 0.82 ,
0.86 , 0.90 , 0.94 , 0.98 , 1.00 };
```
```           for (int x = 0; x < Splitters.Length - 1; x++)
{
if (inValue >= Splitters[x] &&
inValue < Splitters[x + 1])
{
return replacements[x];
}
}
```
```           return inValue;
}
}
```

}</lang>

## C++

<lang C++>#include <iostream>

1. include <cmath>

int main( ) {

```  double froms[ ] = { 0.00 , 0.06 , 0.11 , 0.16 , 0.21 , 0.26 ,
0.31 , 0.36 , 0.41 , 0.46 , 0.51 , 0.56 , 0.61 , 0.66 ,
0.71 , 0.76 , 0.81 , 0.86 , 0.91 , 0.96 } ;
double tos[ ] = { 0.06 , 0.11 , 0.16 , 0.21 , 0.26 , 0.31 ,
0.36 , 0.41 , 0.46 , 0.51 , 0.56 , 0.61 , 0.66 , 0.71 ,
0.76 , 0.81 , 0.86 , 0.91 , 0.96 , 1.01 } ;
double replacements [] = { 0.10 , 0.18 , 0.26 , 0.32 , 0.38 ,
0.44 , 0.50 , 0.54 , 0.58 , 0.62 , 0.66 , 0.70 , 0.74 ,
0.78 , 0.82 , 0.86 , 0.90 , 0.94 , 0.98 , 1.00 } ;
double number = 0.1 ;
std::cout << "Enter a fractional number between 0 and 1 ( 0 to end )!\n" ;
std::cin >> number ;
while ( number != 0 ) {
if ( ( number < 0 ) || ( number > 1 ) ) {
```

std::cout << "Error! Only positive values between 0 and 1 are allowed!\n" ; return 1 ;

```     }
double integerpart = floor ( number ) ;
double remainder = number - integerpart ;
int n = 0 ;
while ( ! ( ( remainder >= froms[ n ] ) && ( remainder < tos[ n ] ) ) )
```

n++ ;

```     std::cout << "-->" << ( integerpart + replacements[ n ] ) << '\n' ;
std::cout << "Enter a fractional number ( 0 to end )!\n" ;
std::cin >> number ;
}
return 0 ;
```

} </lang>

```Sample output:
Enter a fractional number between 0 and 1 ( 0 to end )!
0.7
-->0.78
Enter a fractional number ( 0 to end )!
0.32
-->0.5
Enter a fractional number ( 0 to end )!
0.12
-->0.26
Enter a fractional number ( 0 to end )!
0
```

## Clipper

<lang dbase>FUNCTION PriceFraction( npQuantDispensed )

```   LOCAL aPriceFraction := { {0,.06,.1},;
{.06,.11,.18}, ;
{.11,.16,.26}, ;
{.16,.21,.32}, ;
{.21,.26,.38}, ;
{.26,.31,.44}, ;
{.31,.36,.5}, ;
{.36,.41,.54}, ;
{.41,.46,.58}, ;
{.46,.51,.62}, ;
{.51,.56,.66}, ;
{.56,.61,.7}, ;
{.61,.66,.74}, ;
{.66,.71,.78}, ;
{.71,.76,.82}, ;
{.76,.81,.86}, ;
{.81,.86,.9}, ;
{.86,.91,.94}, ;
{.91,.96,.98} }
LOCAL nResult
LOCAL nScan
IF npQuantDispensed = 0
nResult = 0
ELSEIF npQuantDispensed >= .96
nResult = 1
ELSE
nScan := ASCAN( aPriceFraction, ;
{ |aItem| npQuantDispensed >= aItem[ 1 ] .AND.;
npQuantDispensed <  aItem[ 2 ] } )
nResult := aPriceFraction[ nScan ][ 3 ]
END IF
RETURN nResult</lang>
```

## Erlang

<lang erlang>priceFraction(N) when N < 0 orelse N > 1 ->

```   erlang:error('Values must be between 0 and 1.');
```

priceFraction(N) when N < 0.06 -> 0.10; priceFraction(N) when N < 0.11 -> 0.18; priceFraction(N) when N < 0.16 -> 0.26; priceFraction(N) when N < 0.21 -> 0.32; priceFraction(N) when N < 0.26 -> 0.38; priceFraction(N) when N < 0.31 -> 0.44; priceFraction(N) when N < 0.36 -> 0.50; priceFraction(N) when N < 0.41 -> 0.54; priceFraction(N) when N < 0.46 -> 0.58; priceFraction(N) when N < 0.51 -> 0.62; priceFraction(N) when N < 0.56 -> 0.66; priceFraction(N) when N < 0.61 -> 0.70; priceFraction(N) when N < 0.66 -> 0.74; priceFraction(N) when N < 0.71 -> 0.78; priceFraction(N) when N < 0.76 -> 0.82; priceFraction(N) when N < 0.81 -> 0.86; priceFraction(N) when N < 0.86 -> 0.90; priceFraction(N) when N < 0.91 -> 0.94; priceFraction(N) when N < 0.96 -> 0.98; priceFraction(N) -> 1.00.</lang>

## Fortran

Works with: Fortran version 90 and later

<lang fortran>program price_fraction

``` implicit none
integer, parameter :: i_max = 10
integer :: i
real, dimension (20), parameter :: in =                           &
& (/0.00, 0.06, 0.11, 0.16, 0.21, 0.26, 0.31, 0.36, 0.41, 0.46, &
&   0.51, 0.56, 0.61, 0.66, 0.71, 0.76, 0.81, 0.86, 0.91, 0.96/)
real, dimension (20), parameter :: out =                          &
& (/0.10, 0.18, 0.26, 0.32, 0.38, 0.44, 0.50, 0.54, 0.58, 0.62, &
&   0.66, 0.70, 0.74, 0.78, 0.82, 0.86, 0.90, 0.94, 0.98, 1.00/)
real :: r
```
``` do i = 1, i_max
call random_number (r)
write (*, '(f8.6, 1x, f4.2)') r, out (maxloc (in, r >= in))
end do
```

end program price_fraction</lang> Sample output: <lang>0.997560 1.00 0.566825 0.70 0.965915 1.00 0.747928 0.82 0.367391 0.54 0.480637 0.62 0.073754 0.18 0.005355 0.10 0.347081 0.50 0.342244 0.50</lang>

``` | n < 0 || n > 1 = error "Values must be between 0 and 1."
| n < 0.06 = 0.10
| n < 0.11 = 0.18
| n < 0.16 = 0.26
| n < 0.21 = 0.32
| n < 0.26 = 0.38
| n < 0.31 = 0.44
| n < 0.36 = 0.50
| n < 0.41 = 0.54
| n < 0.46 = 0.58
| n < 0.51 = 0.62
| n < 0.56 = 0.66
| n < 0.61 = 0.70
| n < 0.66 = 0.74
| n < 0.71 = 0.78
| n < 0.76 = 0.82
| n < 0.81 = 0.86
| n < 0.86 = 0.90
| n < 0.91 = 0.94
| n < 0.96 = 0.98
| otherwise = 1.00</lang>
```

Alternative

Translation of: OCaml

:

```   (0.06, 0.10),   (0.11, 0.18),   (0.16, 0.26),   (0.21, 0.32),   (0.26, 0.38),
(0.31, 0.44),   (0.36, 0.50),   (0.41, 0.54),   (0.46, 0.58),   (0.51, 0.62),
(0.56, 0.66),   (0.61, 0.70),   (0.66, 0.74),   (0.71, 0.78),   (0.76, 0.82),
(0.81, 0.86),   (0.86, 0.90),   (0.91, 0.94),   (0.96, 0.98),   (1.01, 1.00),
]
```

price_fraction n

``` | n < 0 || n > 1 = error "Values must be between 0 and 1."
| otherwise = snd \$ head \$ dropWhile ((<= n) . fst) table</lang>
```

## J

Solution: <lang j>le =: -0.96 0.91 0.86 0.81 0.76 0.71 0.66 0.61 0.56 0.51 0.46 0.41 0.36 0.31 0.26 0.21 0.16 0.11 0.06 0.0 out =: 1.00 0.98 0.94 0.90 0.86 0.82 0.78 0.74 0.70 0.66 0.62 0.58 0.54 0.50 0.44 0.38 0.32 0.26 0.18 0.1

priceFraction =: out {~ le I. -</lang>

Example: <lang j> priceFraction 0.34 0.070145 0.06 0.05 0.50214 0.56 1 0.99 0 0.5 0.18 0.18 0.1 0.62 0.7 1 1 0.1</lang>

## OCaml

<lang ocaml>let price_fraction v =

``` if v < 0.0 || v >= 1.01 then
invalid_arg "price_fraction";
let rec aux = function
| (x,r)::tl ->
if v < x then r
else aux tl
| [] -> assert false
in
aux [
0.06, 0.10;   0.11, 0.18;   0.16, 0.26;   0.21, 0.32;   0.26, 0.38;
0.31, 0.44;   0.36, 0.50;   0.41, 0.54;   0.46, 0.58;   0.51, 0.62;
0.56, 0.66;   0.61, 0.70;   0.66, 0.74;   0.71, 0.78;   0.76, 0.82;
0.81, 0.86;   0.86, 0.90;   0.91, 0.94;   0.96, 0.98;   1.01, 1.00;
]</lang>
```

<lang ocaml>let () =

``` let ok_tests = [
(0.3793, 0.54);
(0.4425, 0.58);
(0.0746, 0.18);
(0.6918, 0.78);
(0.2993, 0.44);
(0.5486, 0.66);
(0.7848, 0.86);
(0.9383, 0.98);
(0.2292, 0.38);
] in
Printf.printf " input   res   ok\n";
List.iter (fun (v,ok) ->
let r = price_fraction v in
Printf.printf " %6g  %g  %b\n" v r (r = ok);
) ok_tests;
```
</lang>

## Oz

Using a for-loop with return and a default value for values >= 1.01. For out-of-range input, a "failed value" is returned, i.e. a value that throws an exception when it is accessed.

<lang oz>fun {PriceFraction X}

```  OutOfRange = {Value.failed outOfRange(X)}
```

in

```  for Limit#Result in
[0.00#OutOfRange
0.06#0.10 0.11#0.18 0.16#0.26 0.21#0.32 0.26#0.38 0.31#0.44 0.36#0.5
0.41#0.54 0.46#0.58 0.51#0.62 0.56#0.66 0.61#0.70 0.66#0.74 0.71#0.78
0.76#0.82 0.81#0.86 0.86#0.90 0.91#0.94 0.96#0.98 1.01#1.00
]
return:Return
default:OutOfRange
do
if X < Limit then {Return Result} end
end
```

end</lang>

## PL/I

<lang PL/I> declare t(20) fixed decimal (3,2) static initial (

```  .06, .11, .16, .21, .26, .31, .36, .41, .46,  .51,
.56, .61, .66, .71, .76, .81, .86, .91, .96, 1.01);
```

declare r(20) fixed decimal (3,2) static initial (

```  .10, .18, .26, .32, .38, .44, .50, .54, .58, .62,
.66, .70, .74, .78, .82, .86, .90, .94, .98, 1);
```

declare x float, d fixed decimal (3,2); declare i fixed binary;

loop:

```  do i = 1 to 20;
if x < t(i) then
do; d = r(i); leave loop; end;
end;
```

</lang>

## PureBasic

<lang PureBasic>Procedure.f PriceFraction(price.f)

``` ;returns price unchanged if value is invalid
Protected fraction
Select price * 100
Case 0 To 5
fraction = 10
Case 06 To 10
fraction = 18
Case 11 To 15
fraction = 26
Case 16 To 20
fraction = 32
Case 21 To 25
fraction = 38
Case 26 To 30
fraction = 44
Case 31 To 35
fraction = 5
Case 36 To 40
fraction = 54
Case 41 To 45
fraction = 58
Case 46 To 50
fraction = 62
Case 51 To 55
fraction = 66
Case 56 To 60
fraction = 7
Case 61 To 65
fraction = 74
Case 66 To 70
fraction = 78
Case 71 To 75
fraction = 82
Case 76 To 80
fraction = 86
Case 81 To 85
fraction = 9
Case 86 To 90
fraction = 94
Case 91 To 95
fraction = 98
Case 96 To 100
fraction = 100
Default
ProcedureReturn price
EndSelect

ProcedureReturn fraction / 100
```

EndProcedure

If OpenConsole()

``` Define x.f, i

For i = 1 To 10
x = Random(10000)/10000
PrintN(StrF(x, 4) + " -> " + StrF(PriceFraction(x), 2))
Next

Print(#CRLF\$ + #CRLF\$ + "Press ENTER to exit")
Input()
CloseConsole()
```

EndIf</lang> Sample output:

```0.3793 -> 0.54
0.4425 -> 0.58
0.0746 -> 0.18
0.6918 -> 0.78
0.2993 -> 0.44
0.5486 -> 0.66
0.7848 -> 0.86
0.9383 -> 0.98
0.2292 -> 0.38
0.9560 -> 1.00```

## Python

Using the bisect standard module to reduce the comparisons with members of the cin array.

<lang python>>>> import bisect >>> _cin = [.06, .11, .16, .21, .26, .31, .36, .41, .46, .51, .56, .61, .66, .71, .76, .81, .86, .91, .96, 1.01] >>> _cout = [.10, .18, .26, .32, .38, .44, .50, .54, .58, .62, .66, .70, .74, .78, .82, .86, .90, .94, .98, 1.00] >>> def pricerounder(pricein): return _cout[ bisect.bisect_right(_cin, pricein) ]</lang>

When dealing with money it is good to think about possible loss of precision. If we change the units to be integer cents we could use the following exact routine: <lang python>>>> import bisect >>> _cin = [ 6, 11, 16, 21, 26, 31, 36, 41, 46, 51, 56, 61, 66, 71, 76, 81, 86, 91, 96, 101] >>> _cout = [10, 18, 26, 32, 38, 44, 50, 54, 57, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 100] >>> def centsrounder(centsin): return _cout[ bisect.bisect_right(_cin, centsin) ]</lang> Other options are to use the fractions or decimals modules for calculating money to a known precision.

Bisection library code

The `bisect` Python standard library function uses the following code that improves on a simple linear scan through a sorted list:
<lang python>def bisect_right(a, x, lo=0, hi=None):
```   """Return the index where to insert item x in list a, assuming a is sorted.
```
```   The return value i is such that all e in a[:i] have e <= x, and all e in
a[i:] have e > x.  So if x already appears in the list, a.insert(x) will
insert just after the rightmost x already there.
```
```   Optional args lo (default 0) and hi (default len(a)) bound the
slice of a to be searched.
"""
```
```   if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
if x < a[mid]: hi = mid
else: lo = mid+1
return lo</lang>
```

## Ruby

A simple function with hardcoded values. <lang ruby>def rescale_price_fraction(value)

``` raise ArgumentError, "value=#{value}, must have: 0 <= value < 1.01" if value < 0 || value >= 1.01
if     value < 0.06  then  0.10
elsif  value < 0.11  then  0.18
elsif  value < 0.16  then  0.26
elsif  value < 0.21  then  0.32
elsif  value < 0.26  then  0.38
elsif  value < 0.31  then  0.44
elsif  value < 0.36  then  0.50
elsif  value < 0.41  then  0.54
elsif  value < 0.46  then  0.58
elsif  value < 0.51  then  0.62
elsif  value < 0.56  then  0.66
elsif  value < 0.61  then  0.70
elsif  value < 0.66  then  0.74
elsif  value < 0.71  then  0.78
elsif  value < 0.76  then  0.82
elsif  value < 0.81  then  0.86
elsif  value < 0.86  then  0.90
elsif  value < 0.91  then  0.94
elsif  value < 0.96  then  0.98
elsif  value < 1.01  then  1.00
end
```

end</lang>

Or, where we can cut and paste the textual table in one place

Works with: Ruby version 1.8.7+

for the `String#lines` method.

For Ruby 1.8.6, use `String#each_line`

<lang ruby>class Price

``` ConversionTable = <<-END_OF_TABLE
>=  0.00  <  0.06  :=  0.10
>=  0.06  <  0.11  :=  0.18
>=  0.11  <  0.16  :=  0.26
>=  0.16  <  0.21  :=  0.32
>=  0.21  <  0.26  :=  0.38
>=  0.26  <  0.31  :=  0.44
>=  0.31  <  0.36  :=  0.50
>=  0.36  <  0.41  :=  0.54
>=  0.41  <  0.46  :=  0.58
>=  0.46  <  0.51  :=  0.62
>=  0.51  <  0.56  :=  0.66
>=  0.56  <  0.61  :=  0.70
>=  0.61  <  0.66  :=  0.74
>=  0.66  <  0.71  :=  0.78
>=  0.71  <  0.76  :=  0.82
>=  0.76  <  0.81  :=  0.86
>=  0.81  <  0.86  :=  0.90
>=  0.86  <  0.91  :=  0.94
>=  0.91  <  0.96  :=  0.98
>=  0.96  <  1.01  :=  1.00
END_OF_TABLE
```
``` RE = %r{ ([<>=]+) \s* (\d\.\d\d) \s* ([<>=]+) \s* (\d\.\d\d) \D+ (\d\.\d\d) }x
```
``` # extract the comparison operators and numbers from the table
CONVERSION_TABLE = ConversionTable.lines.inject([]) do |table, line|
m = line.match(RE)
if not m.nil? and m.length == 6
table << [m[1], m[2].to_f, m[3], m[4].to_f, m[5].to_f]
end
table
end
```
``` MIN_COMP, MIN = CONVERSION_TABLE[0][0..1]
MAX_COMP, MAX = CONVERSION_TABLE[-1][2..3]
```
``` def initialize(value)
if (not value.send(MIN_COMP, MIN)) or (not value.send(MAX_COMP, MAX))
raise ArgumentError, "value=#{value}, must have: #{MIN} #{MIN_COMP} value #{MAX_COMP} #{MAX}"
end
@standard_value = CONVERSION_TABLE.find do |comp1, lower, comp2, upper, standard|
value.send(comp1, lower) and value.send(comp2, upper)
end.last
end
```

end</lang>

And a test suite <lang ruby>require 'test/unit'

class PriceFractionTests < Test::Unit::TestCase

``` @@ok_tests = [
[0.3793, 0.54],
[0.4425, 0.58],
[0.0746, 0.18],
[0.6918, 0.78],
[0.2993, 0.44],
[0.5486, 0.66],
[0.7848, 0.86],
[0.9383, 0.98],
[0.2292, 0.38],
]
```
``` def test_ok
@@ok_tests.each do |val, exp|
assert_equal(exp, rescale_price_fraction(val))
assert_equal(exp, Price.new(val).standard_value)
end
assert_raise(ArgumentError) {rescale_price_fraction(val)}
assert_raise(ArgumentError) {Price.new(val).standard_value}
end
end
```

end</lang>

output

```Loaded suite price_fraction
Started
.
Finished in 0.001000 seconds.

1 tests, 22 assertions, 0 failures, 0 errors, 0 skips```

## Tcl

Structured as two functions, one to parse the input data as described in the problem into a form which Tcl can work with easily, and the other to perform the mapping. <lang tcl># Used once to turn the table into a "nice" form proc parseTable table {

```   set map {}
set LINE_RE {^ *>= *([0-9.]+) *< *([0-9.]+) *:= *([0-9.]+) *\$}
foreach line [split \$table \n] {
```

if {[string trim \$line] eq ""} continue if {[regexp \$LINE_RE \$line -> min max target]} { lappend map \$min \$max \$target } else { error "invalid table format: \$line" }

```   }
return \$map
```

}

1. How to apply the "nice" table to a particular value

proc priceFraction {map value} {

```   foreach {minimum maximum target} \$map {
```

if {\$value >= \$minimum && \$value < \$maximum} {return \$target}

```   }
# Failed to map; return the input
return \$value
```

}</lang> How it is used: <lang tcl># Make the mapping set inputTable {

```   >=  0.00  <  0.06  :=  0.10
>=  0.06  <  0.11  :=  0.18
>=  0.11  <  0.16  :=  0.26
>=  0.16  <  0.21  :=  0.32
>=  0.21  <  0.26  :=  0.38
>=  0.26  <  0.31  :=  0.44
>=  0.31  <  0.36  :=  0.50
>=  0.36  <  0.41  :=  0.54
>=  0.41  <  0.46  :=  0.58
>=  0.46  <  0.51  :=  0.62
>=  0.51  <  0.56  :=  0.66
>=  0.56  <  0.61  :=  0.70
>=  0.61  <  0.66  :=  0.74
>=  0.66  <  0.71  :=  0.78
>=  0.71  <  0.76  :=  0.82
>=  0.76  <  0.81  :=  0.86
>=  0.81  <  0.86  :=  0.90
>=  0.86  <  0.91  :=  0.94
>=  0.91  <  0.96  :=  0.98
>=  0.96  <  1.01  :=  1.00
```

} set map [parseTable \$inputTable]

1. Apply the mapping to some inputs (from the Oz example)

foreach example {.7388727 .8593103 .826687 .3444635 .0491907} {

```   puts "\$example -> [priceFraction \$map \$example]"
```

}</lang> Output:

```.7388727 -> 0.82
.8593103 -> 0.90
.826687 -> 0.90
.3444635 -> 0.50
.0491907 -> 0.10
```

## Ursala

<lang Ursala>#import flo

le = <0.06,.11,.16,.21,.26,.31,.36,.41,.46,.51,.56,.61,.66,.71,.76,.81,.86,.91,.96,1.01> out = <0.10,.18,.26,.32,.38,.44,.50,.54,.58,.62,.66,.70,.74,.78,.82,.86,.90,.94,.98,1.>

price_fraction = fleq@rlPlX*|rhr\~&p(le,out)</lang> main points:

• `~&p(le,out)` zips the pair of lists `le` and `out` into a list of pairs
• A function of the form `f\y` applied to an argument `x` evaluates to `f(x,y)`
• A function of the form `f*|` applied to a pair `(x,y)` where `y` is a list, makes a list of pairs with `x` on the left of each item and an item of `y` on the right. Then it applies `f` to each pair, makes a list of the right sides of those for which `f` returned true, and makes a separate list of the right sides of those for which `f` returned false.
• The suffix `rhr` after the `*|` operator extracts the right side of the head of the right list from the result.
• The operand to the `*|` operator, `fleq@rlPlX` is the less-or-equal predicate on floating point numbers, composed with the function `~&rlPlX` which transforms a triple `(u,(v,w))` to `(v,u)`

test program: <lang Ursala>#cast %eL

test = price_fraction* <0.34,0.070145,0.06,0.05,0.50214,0.56,1.,0.99,0.> </lang> output:

```<
5.000000e-01,
1.800000e-01,
1.800000e-01,
1.000000e-01,
6.200000e-01,
7.000000e-01,
1.000000e+00,
1.000000e+00,
1.000000e-01>```