Skip to content
This repository has been archived by the owner on Aug 13, 2020. It is now read-only.

Adds Brazilian Portuguese #37

Open
wants to merge 41 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
458f2b9
Added a single Brazilian Portuguese example
mrinaldi Feb 24, 2016
b34c9ed
Added one to nine
mrinaldi Feb 24, 2016
c7fe4f9
Handled casing and whitespaces
mrinaldi Feb 24, 2016
89919fc
Added ten to nineteen
mrinaldi Feb 24, 2016
fa20232
Added twenty
mrinaldi Feb 24, 2016
a630e22
Added thirty
mrinaldi Feb 25, 2016
73158c7
Added forthy
mrinaldi Feb 25, 2016
0de57a6
Added fifty
mrinaldi Feb 25, 2016
149837e
Added sixty
mrinaldi Feb 25, 2016
44b66a9
Added seventy
mrinaldi Feb 25, 2016
8784e41
Added eighty
mrinaldi Feb 25, 2016
6e89a98
Added ninety
mrinaldi Feb 25, 2016
f07f6b9
Added one hundred
mrinaldi Feb 25, 2016
e0b5282
Added two hundred
mrinaldi Feb 25, 2016
e98fbb5
Added three hundred
mrinaldi Feb 25, 2016
906690a
Added four hundred
mrinaldi Feb 25, 2016
0645d7a
Added five hundred
mrinaldi Feb 25, 2016
0c3fcc9
Added six hundred
mrinaldi Feb 25, 2016
78aeed6
Simplifying four and six hundred
mrinaldi Feb 25, 2016
8488f9c
Added seven, eight and nine hundred
mrinaldi Feb 25, 2016
ad14604
Added thousand
mrinaldi Feb 25, 2016
9a088c9
Added thousands
mrinaldi Feb 25, 2016
0a76e9b
Added million
mrinaldi Feb 25, 2016
c542cef
Added millions
mrinaldi Feb 25, 2016
53f1e0d
Added billions
mrinaldi Feb 25, 2016
572f4c0
Added a single toBrazilian example
mrinaldi Feb 25, 2016
0443edf
Added one to twenty
mrinaldi Feb 25, 2016
20b1bf1
Added twenty
mrinaldi Feb 25, 2016
26db7a9
Added thirty to ninety
mrinaldi Feb 25, 2016
3f96eae
Added one hundred
mrinaldi Feb 25, 2016
862992f
Added hundreds
mrinaldi Feb 25, 2016
0155082
Added thousands
mrinaldi Feb 25, 2016
505f53b
Added rule that hundreds and thousands have comma inbetween
mrinaldi Feb 25, 2016
a4a2ffd
Added rule that hundreds and thousands have "and" inbetween
mrinaldi Feb 25, 2016
9060dd8
Added millions
mrinaldi Feb 25, 2016
d5694a3
Added billions
mrinaldi Feb 25, 2016
9fd13a0
Cleaning up
mrinaldi Feb 25, 2016
d036ce5
Added minus
mrinaldi Feb 25, 2016
2533412
Added BrazilianNumeralConverter
mrinaldi Feb 25, 2016
5e539f5
Added examples that should not be parsed
mrinaldi Feb 29, 2016
3eaec0e
Tests Numeral Converter properties against Brazilian Portuguese
mrinaldi Feb 29, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Numsense.UnitTests.CSharp/NumeralConverterProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,11 @@ public static Arbitrary<ConverterPropertyGroup> Converter()
new ConverterPropertyGroup(
new GermanNumeralConverter(),
NumeralModule.toGerman,
NumeralModule.tryParseGerman)
NumeralModule.tryParseGerman),
new ConverterPropertyGroup(
new BrazilianNumeralConverter(),
NumeralModule.toBrazilian,
NumeralModule.tryParseBrazilian)
)
.ToArbitrary();
}
Expand Down
15 changes: 15 additions & 0 deletions Numsense.UnitTests.CSharp/NumeralTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,20 @@ public void GermanIsSingleton()
var actual = Numeral.German;
Assert.Same(expected, actual);
}

[Fact]
public void BrazilianIsCorrect()
{
var actual = Numeral.Brazilian;
Assert.IsAssignableFrom<BrazilianNumeralConverter>(actual);
}

[Fact]
public void BrazilianIsSingleton()
{
var expected = Numeral.Brazilian;
var actual = Numeral.Brazilian;
Assert.Same(expected, actual);
}
}
}
302 changes: 302 additions & 0 deletions Numsense.UnitTests/BrazilianPortugueseExamples.fs

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions Numsense.UnitTests/NumeralProperties.fs
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,18 @@ let ``negative German is the inverse of positive German`` x =

sprintf "minus-%s" (Numeral.toGerman x) =! actualGerman
Some -x =! actualInteger

[<Property(QuietOnSuccess = true)>]
let ``tryParseBrazilian is the inverse of toBrazilian`` x =
test <@ Some x = (x |> Numeral.toBrazilian |> Numeral.tryParseBrazilian) @>

[<Property(QuietOnSuccess = true)>]
let ``negative Brazilian is the inverse of positive Brazilian`` x =
x <> 0 ==> lazy
let x = abs x

let actualBrazilian = Numeral.toBrazilian -x
let actualInteger = Numeral.tryParseBrazilian actualBrazilian

sprintf "menos %s" (Numeral.toBrazilian x) =! actualBrazilian
Some -x =! actualInteger
1 change: 1 addition & 0 deletions Numsense.UnitTests/Numsense.UnitTests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
<Compile Include="AssemblyInfo.fs" />
<Compile Include="UseCultureAttribute.fs" />
<Compile Include="NumeralProperties.fs" />
<Compile Include="BrazilianPortugueseExamples.fs" />
<Compile Include="BulgarianExamples.fs" />
<Compile Include="DanishExamples.fs" />
<Compile Include="EnglishExamples.fs" />
Expand Down
136 changes: 136 additions & 0 deletions Numsense/BrazilianPortuguese.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
module internal Ploeh.Numsense.BrazilianPortuguese

open Ploeh.Numsense.InternalDsl

let rec internal toBrazilianImp x =
let formatPrefix prefix factor x =
let remainder = x % factor

let rec hundreds x =
if x > 1000
then hundreds (x / 1000)
else x

match remainder, hundreds remainder with
| 0, _ -> prefix
| _, r when r > 100 && r % 100 <> 0 ->
sprintf "%s, %s" prefix <| toBrazilianImp remainder
| _ -> sprintf "%s e %s" prefix <| toBrazilianImp remainder

let formatSuffix suffix factor x =
let prefix = sprintf "%s%s" (toBrazilianImp (x / factor)) suffix
formatPrefix prefix factor x

match x with
| x when x < 0 -> sprintf "menos %s" <| toBrazilianImp -x
| 0 -> "zero"
| 1 -> "um"
| 2 -> "dois"
| 3 -> "três"
| 4 -> "quatro"
| 5 -> "cinco"
| 6 -> "seis"
| 7 -> "sete"
| 8 -> "oito"
| 9 -> "nove"
| 10 -> "dez"
| 11 -> "onze"
| 12 -> "doze"
| 13 -> "treze"
| 14 -> "quatorze"
| 15 -> "quinze"
| 16 -> "dezesseis"
| 17 -> "dezessete"
| 18 -> "dezoito"
| 19 -> "dezenove"
| Between 20 30 x -> formatPrefix "vinte" 10 x
| Between 30 40 x -> formatPrefix "trinta" 10 x
| Between 40 50 x -> formatPrefix "quarenta" 10 x
| Between 50 60 x -> formatPrefix "cinquenta" 10 x
| Between 60 70 x -> formatPrefix "sessenta" 10 x
| Between 70 80 x -> formatPrefix "setenta" 10 x
| Between 80 90 x -> formatPrefix "oitenta" 10 x
| Between 90 100 x -> formatPrefix "noventa" 10 x
| 100 -> "cem"
| Between 100 200 x -> formatPrefix "cento" 100 x
| Between 200 300 x -> formatPrefix "duzentos" 100 x
| Between 300 400 x -> formatPrefix "trezentos" 100 x
| Between 500 600 x -> formatPrefix "quinhentos" 100 x
| Between 400 1000 x -> formatSuffix "centos" 100 x
| Between 1000 2000 x -> formatPrefix "mil" 1000 x
| Between 1000 1000000 x -> formatSuffix " mil" 1000 x
| Between 1000000 2000000 x -> formatPrefix "um milhão" 1000000 x
| Between 2000000 1000000000 x -> formatSuffix " milhões" 1000000 x
| Between 1000000000 2000000000 x -> formatPrefix "um bilhão" 1000000000 x
| _ -> formatSuffix " bilhões" 1000000000 x

let internal tryParseBrazilianImp (x : string) =
let rec conv acc candidate =
let conv' remainderFactor placeFactor acc' candidate =
if (acc % remainderFactor) / placeFactor <> 0
then None
else conv acc' candidate

let convBillions acc' candidate =
if acc / 1000000000 <> 0
then None
else conv acc' candidate

let convMillions = conv' 1000000000 1000000
let convThousands = conv' 1000000 1000
let convHundreds = conv' 1000 100
let convTens = conv' 100 10
let convUnits = conv' 10 1

match candidate with
| "" -> Some acc
| StartsWith " " t
| StartsWith "," t
| StartsWith "E" t -> conv acc t
| "ZERO" -> Some (0 + acc)
| StartsWith "BILHÃO" t
| StartsWith "BILHÕES" t -> convBillions (1000000000 %* acc) t
| StartsWith "MILHÃO" t
| StartsWith "MILHÕES" t -> convMillions (1000000 %* acc) t
| StartsWith "MIL" t -> convThousands (1000 %* acc) t
| StartsWith "CEM" t -> convHundreds (100 + acc) t
| StartsWith "CENTOS" t -> convHundreds (100 %* acc) t
| StartsWith "CENTO" t -> convHundreds (100 + acc) t
| StartsWith "DUZENTOS" t -> convHundreds (200 + acc) t
| StartsWith "TREZENTOS" t -> convHundreds (300 + acc) t
| StartsWith "QUINHENTOS" t -> convHundreds (500 + acc) t
| StartsWith "VINTE" t -> convTens (20 + acc) t
| StartsWith "TRINTA" t -> convTens (30 + acc) t
| StartsWith "QUARENTA" t -> convTens (40 + acc) t
| StartsWith "CINQUENTA" t -> convTens (50 + acc) t
| StartsWith "CINQÜENTA" t -> convTens (50 + acc) t
| StartsWith "SESSENTA" t -> convTens (60 + acc) t
| StartsWith "SETENTA" t -> convTens (70 + acc) t
| StartsWith "OITENTA" t -> convTens (80 + acc) t
| StartsWith "NOVENTA" t -> convTens (90 + acc) t
| StartsWith "ONZE" t -> convTens (11 + acc) t
| StartsWith "DOZE" t -> convTens (12 + acc) t
| StartsWith "TREZE" t -> convTens (13 + acc) t
| StartsWith "CATORZE" t -> convTens (14 + acc) t
| StartsWith "QUATORZE" t -> convTens (14 + acc) t
| StartsWith "QUINZE" t -> convTens (15 + acc) t
| StartsWith "DEZESSEIS" t -> convTens (16 + acc) t
| StartsWith "DEZESSETE" t -> convTens (17 + acc) t
| StartsWith "DEZOITO" t -> convTens (18 + acc) t
| StartsWith "DEZENOVE" t -> convTens (19 + acc) t
| StartsWith "DEZ" t -> convTens (10 + acc) t
| StartsWith "UM" t -> convUnits (1 + acc) t
| StartsWith "DOIS" t -> convUnits (2 + acc) t
| StartsWith "TRÊS" t -> convUnits (3 + acc) t
| StartsWith "QUATRO" t -> convUnits (4 + acc) t
| StartsWith "CINCO" t -> convUnits (5 + acc) t
| StartsWith "SEIS" t -> convUnits (6 + acc) t
| StartsWith "SETE" t -> convUnits (7 + acc) t
| StartsWith "OITO" t -> convUnits (8 + acc) t
| StartsWith "NOVE" t -> convUnits (9 + acc) t
| _ -> None

let canonicalized = x.Trim().ToUpper(System.Globalization.CultureInfo "pt-BR")
match canonicalized with
| StartsWith "MENOS" t -> conv 0 t |> Option.map (~-)
| _ -> conv 0 canonicalized
3 changes: 3 additions & 0 deletions Numsense/Numeral.fs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ let tryParseGerman = German.tryParseGermanImp

let toPortuguese = Portuguese.toPortugueseImp
let tryParsePortuguese = Portuguese.tryParsePortugueseImp

let tryParseBrazilian = BrazilianPortuguese.tryParseBrazilianImp
let toBrazilian = BrazilianPortuguese.toBrazilianImp
1 change: 1 addition & 0 deletions Numsense/Numsense.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
<ItemGroup>
<Compile Include="AssemblyInfo.fs" />
<Compile Include="InternalDsl.fs" />
<Compile Include="BrazilianPortuguese.fs" />
<Compile Include="Bulgarian.fs" />
<Compile Include="Danish.fs" />
<Compile Include="English.fs" />
Expand Down
7 changes: 7 additions & 0 deletions Numsense/ObjectOriented.fs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ type PortugueseNumeralConverter () =
member this.TryParse (s, result) =
Helper.tryParse Numeral.tryParsePortuguese (s, &result)

type BrazilianNumeralConverter () =
interface INumeralConverter with
member this.ToNumeral number = Numeral.toBrazilian number
member this.TryParse (s, result) =
Helper.tryParse Numeral.tryParseBrazilian (s, &result)

type Numeral private () =
static member val Bulgarian = BulgarianNumeralConverter () :> INumeralConverter
static member val English = EnglishNumeralConverter () :> INumeralConverter
Expand All @@ -106,3 +112,4 @@ type Numeral private () =
static member val Romanian = RomanianNumeralConverter () :> INumeralConverter
static member val German = GermanNumeralConverter () :> INumeralConverter
static member val Portuguese = PortugueseNumeralConverter () :> INumeralConverter
static member val Brazilian = BrazilianNumeralConverter () :> INumeralConverter