//Есть продукты A, B, C, D, E, F, G, H, I, J, K, L, M. Каждый продукт стоит определенную сумму.
//
//Есть набор правил расчета итоговой суммы:
//
//Если одновременно выбраны А и B, то их суммарная стоимость уменьшается на 10% (для каждой пары А и B)
//Если одновременно выбраны D и E, то их суммарная стоимость уменьшается на 5% (для каждой пары D и E)
//Если одновременно выбраны E,F,G, то их суммарная стоимость уменьшается на 5% (для каждой тройки E,F,G)
//Если одновременно выбраны А и один из [K,L,M], то стоимость выбранного продукта уменьшается на 5%
//Если пользователь выбрал одновременно 3 продукта, он получает скидку 5% от суммы заказа
//Если пользователь выбрал одновременно 4 продукта, он получает скидку 10% от суммы заказа
//Если пользователь выбрал одновременно 5 продуктов, он получает скидку 20% от суммы заказа
//
//Описанные скидки 5,6,7 не суммируются, применяется только одна из них
//Продукты A и C не участвуют в скидках 5,6,7
//Каждый товар может участвовать только в одной скидке. Скидки применяются последовательно в порядке описанном выше.
case class Product
(name
: String, price
: Double
)
case class DiscountResult
(discountAction
: Double
=> Double, usedProducts
: Seq
[Product
])
def calc
(order
: Seq
[Product
]): DiscountResult
}
//@tailrec // may be optimized
def repeat
[T
](value
: Seq
[T
], count
: Int
): Seq
[T
] = { else value ++ repeat
(value, count -
1) }
class ConcreteProductsDiscount
(products
: Set
[Product
], discount
: Double
) extends Discount
{ def calc
(order
: Seq
[Product
]) = { val bunchCount
= products.
map(p
=> order.
count(_ == p
)).
min val d
= bunchCount
* products.
toSeq.
map(_.
price).
sum * discount
DiscountResult({ _ - d }, repeat(products.toSeq, bunchCount))
}
}
class OrderCountDiscount
(apply
: Int
=> Boolean, ignoredProducts
: Set
[Product
], discount
: Double
) extends Discount
{ def calc
(order
: Seq
[Product
]) = { val count
= order.
count(!ignoredProducts.
contains(_)) val multiplier
= if (apply
(count
)) 1 - discount
else 1 DiscountResult({ _ * multiplier }, order)
}
}
def calculate
(order
: Seq
[Product
], discounts
: Seq
[Discount
]): Double
= { val initPrice
= order.
map(_.
price).
sum val foldedOrder
= discounts.
foldLeft((initPrice, order
)) { (acc, discount
) => discount.
calc(acc.
_2
) match { case DiscountResult
(f, used
) => (f
(acc.
_1
), acc.
_2 diff used
) }
}
foldedOrder._1
}
val A
= Product
("A",
100) val B
= Product
("B",
100) val C
= Product
("C",
100) val D
= Product
("D",
100) val E
= Product
("E",
100) val F
= Product
("F",
100) val G
= Product
("G",
100) val H
= Product
("H",
100) val I
= Product
("I",
100) val J
= Product
("J",
100) val K
= Product
("K",
100) val L
= Product
("L",
100) val M
= Product
("M",
100)
val discounts
= Seq
(new ConcreteProductsDiscount
(Set
(A, B
),
0.1),
new ConcreteProductsDiscount
(Set
(D, E
),
0.05),
new ConcreteProductsDiscount
(Set
(E, F, G
),
0.05),
new OrderCountDiscount
({ _ == 3}, Set
(A, C
),
0.05),
new OrderCountDiscount
({ _ == 4}, Set
(A, C
),
0.1),
new OrderCountDiscount
({ _ >= 5}, Set
(A, C
),
0.2))
println(calculate(Seq(A, B, A, B, K, K, K, E, F, G), discounts))
}
b2JqZWN0IE1haW4gZXh0ZW5kcyBBcHAgewovL9CV0YHRgtGMINC/0YDQvtC00YPQutGC0YsgQSwgQiwgQywgRCwgRSwgRiwgRywgSCwgSSwgSiwgSywgTCwgTS4g0JrQsNC20LTRi9C5INC/0YDQvtC00YPQutGCINGB0YLQvtC40YIg0L7Qv9GA0LXQtNC10LvQtdC90L3Rg9GOINGB0YPQvNC80YMuCi8vCi8v0JXRgdGC0Ywg0L3QsNCx0L7RgCDQv9GA0LDQstC40Lsg0YDQsNGB0YfQtdGC0LAg0LjRgtC+0LPQvtCy0L7QuSDRgdGD0LzQvNGLOgovLwovL9CV0YHQu9C4INC+0LTQvdC+0LLRgNC10LzQtdC90L3QviDQstGL0LHRgNCw0L3RiyDQkCDQuCBCLCDRgtC+INC40YUg0YHRg9C80LzQsNGA0L3QsNGPINGB0YLQvtC40LzQvtGB0YLRjCDRg9C80LXQvdGM0YjQsNC10YLRgdGPINC90LAgMTAlICjQtNC70Y8g0LrQsNC20LTQvtC5INC/0LDRgNGLINCQINC4IEIpCi8v0JXRgdC70Lgg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+INCy0YvQsdGA0LDQvdGLIEQg0LggRSwg0YLQviDQuNGFINGB0YPQvNC80LDRgNC90LDRjyDRgdGC0L7QuNC80L7RgdGC0Ywg0YPQvNC10L3RjNGI0LDQtdGC0YHRjyDQvdCwIDUlICjQtNC70Y8g0LrQsNC20LTQvtC5INC/0LDRgNGLIEQg0LggRSkKLy/QldGB0LvQuCDQvtC00L3QvtCy0YDQtdC80LXQvdC90L4g0LLRi9Cx0YDQsNC90YsgRSxGLEcsINGC0L4g0LjRhSDRgdGD0LzQvNCw0YDQvdCw0Y8g0YHRgtC+0LjQvNC+0YHRgtGMINGD0LzQtdC90YzRiNCw0LXRgtGB0Y8g0L3QsCA1JSAo0LTQu9GPINC60LDQttC00L7QuSDRgtGA0L7QudC60LggRSxGLEcpCi8v0JXRgdC70Lgg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+INCy0YvQsdGA0LDQvdGLINCQINC4INC+0LTQuNC9INC40LcgW0ssTCxNXSwg0YLQviDRgdGC0L7QuNC80L7RgdGC0Ywg0LLRi9Cx0YDQsNC90L3QvtCz0L4g0L/RgNC+0LTRg9C60YLQsCDRg9C80LXQvdGM0YjQsNC10YLRgdGPINC90LAgNSUKLy/QldGB0LvQuCDQv9C+0LvRjNC30L7QstCw0YLQtdC70Ywg0LLRi9Cx0YDQsNC7INC+0LTQvdC+0LLRgNC10LzQtdC90L3QviAzINC/0YDQvtC00YPQutGC0LAsINC+0L0g0L/QvtC70YPRh9Cw0LXRgiDRgdC60LjQtNC60YMgNSUg0L7RgiDRgdGD0LzQvNGLINC30LDQutCw0LfQsAovL9CV0YHQu9C4INC/0L7Qu9GM0LfQvtCy0LDRgtC10LvRjCDQstGL0LHRgNCw0Lsg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+IDQg0L/RgNC+0LTRg9C60YLQsCwg0L7QvSDQv9C+0LvRg9GH0LDQtdGCINGB0LrQuNC00LrRgyAxMCUg0L7RgiDRgdGD0LzQvNGLINC30LDQutCw0LfQsAovL9CV0YHQu9C4INC/0L7Qu9GM0LfQvtCy0LDRgtC10LvRjCDQstGL0LHRgNCw0Lsg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+IDUg0L/RgNC+0LTRg9C60YLQvtCyLCDQvtC9INC/0L7Qu9GD0YfQsNC10YIg0YHQutC40LTQutGDIDIwJSDQvtGCINGB0YPQvNC80Ysg0LfQsNC60LDQt9CwCi8vCi8v0J7Qv9C40YHQsNC90L3Ri9C1INGB0LrQuNC00LrQuCA1LDYsNyDQvdC1INGB0YPQvNC80LjRgNGD0Y7RgtGB0Y8sINC/0YDQuNC80LXQvdGP0LXRgtGB0Y8g0YLQvtC70YzQutC+INC+0LTQvdCwINC40Lcg0L3QuNGFCi8v0J/RgNC+0LTRg9C60YLRiyBBINC4IEMg0L3QtSDRg9GH0LDRgdGC0LLRg9GO0YIg0LIg0YHQutC40LTQutCw0YUgNSw2LDcKLy/QmtCw0LbQtNGL0Lkg0YLQvtCy0LDRgCDQvNC+0LbQtdGCINGD0YfQsNGB0YLQstC+0LLQsNGC0Ywg0YLQvtC70YzQutC+INCyINC+0LTQvdC+0Lkg0YHQutC40LTQutC1LiDQodC60LjQtNC60Lgg0L/RgNC40LzQtdC90Y/RjtGC0YHRjyDQv9C+0YHQu9C10LTQvtCy0LDRgtC10LvRjNC90L4g0LIg0L/QvtGA0Y/QtNC60LUg0L7Qv9C40YHQsNC90L3QvtC8INCy0YvRiNC1LgoKY2FzZSBjbGFzcyBQcm9kdWN0KG5hbWU6IFN0cmluZywgcHJpY2U6IERvdWJsZSkKCmNhc2UgY2xhc3MgRGlzY291bnRSZXN1bHQoZGlzY291bnRBY3Rpb246IERvdWJsZSA9PiBEb3VibGUsIHVzZWRQcm9kdWN0czogU2VxW1Byb2R1Y3RdKQoKdHJhaXQgRGlzY291bnR7CiAgZGVmIGNhbGMob3JkZXI6IFNlcVtQcm9kdWN0XSk6IERpc2NvdW50UmVzdWx0Cn0KCi8vQHRhaWxyZWMgLy8gbWF5IGJlIG9wdGltaXplZApkZWYgcmVwZWF0W1RdKHZhbHVlOiBTZXFbVF0sIGNvdW50OiBJbnQpOiBTZXFbVF0gPSB7CiAgaWYoY291bnQgPT0gMCkgU2VxKCkKICBlbHNlIGlmKGNvdW50ID09IDEpIHZhbHVlCiAgZWxzZSB2YWx1ZSArKyByZXBlYXQodmFsdWUsIGNvdW50IC0gMSkKfQoKY2xhc3MgQ29uY3JldGVQcm9kdWN0c0Rpc2NvdW50KHByb2R1Y3RzOiBTZXRbUHJvZHVjdF0sIGRpc2NvdW50OiBEb3VibGUpIGV4dGVuZHMgRGlzY291bnR7CiAgZGVmIGNhbGMob3JkZXI6IFNlcVtQcm9kdWN0XSkgPSB7CiAgICB2YWwgYnVuY2hDb3VudCA9IHByb2R1Y3RzLm1hcChwID0+IG9yZGVyLmNvdW50KF8gPT0gcCkpLm1pbgogICAgdmFsIGQgPSBidW5jaENvdW50ICogcHJvZHVjdHMudG9TZXEubWFwKF8ucHJpY2UpLnN1bSAqIGRpc2NvdW50CiAgICBEaXNjb3VudFJlc3VsdCh7IF8gLSBkIH0sIHJlcGVhdChwcm9kdWN0cy50b1NlcSwgYnVuY2hDb3VudCkpCiAgfQp9CgpjbGFzcyBPcmRlckNvdW50RGlzY291bnQoYXBwbHk6IEludCA9PiBCb29sZWFuLCBpZ25vcmVkUHJvZHVjdHM6IFNldFtQcm9kdWN0XSwgZGlzY291bnQ6IERvdWJsZSkgZXh0ZW5kcyBEaXNjb3VudHsKICBkZWYgY2FsYyhvcmRlcjogU2VxW1Byb2R1Y3RdKSA9IHsKICAgIHZhbCBjb3VudCA9IG9yZGVyLmNvdW50KCFpZ25vcmVkUHJvZHVjdHMuY29udGFpbnMoXykpCiAgICB2YWwgbXVsdGlwbGllciA9IGlmIChhcHBseShjb3VudCkpIDEgLSBkaXNjb3VudCBlbHNlIDEKICAgIERpc2NvdW50UmVzdWx0KHsgXyAqIG11bHRpcGxpZXIgfSwgb3JkZXIpCiAgfQp9CgpkZWYgY2FsY3VsYXRlKG9yZGVyOiBTZXFbUHJvZHVjdF0sIGRpc2NvdW50czogU2VxW0Rpc2NvdW50XSk6IERvdWJsZSA9IHsKICB2YWwgaW5pdFByaWNlID0gb3JkZXIubWFwKF8ucHJpY2UpLnN1bQogIHZhbCBmb2xkZWRPcmRlciA9IGRpc2NvdW50cy5mb2xkTGVmdCgoaW5pdFByaWNlLCBvcmRlcikpIHsKICAgIChhY2MsIGRpc2NvdW50KSA9PiBkaXNjb3VudC5jYWxjKGFjYy5fMikgbWF0Y2ggewogICAgICBjYXNlIERpc2NvdW50UmVzdWx0KGYsIHVzZWQpID0+IChmKGFjYy5fMSksIGFjYy5fMiBkaWZmIHVzZWQpCiAgICB9CiAgfQogIGZvbGRlZE9yZGVyLl8xCn0KCnZhbCBBID0gUHJvZHVjdCgiQSIsIDEwMCkKdmFsIEIgPSBQcm9kdWN0KCJCIiwgMTAwKQp2YWwgQyA9IFByb2R1Y3QoIkMiLCAxMDApCnZhbCBEID0gUHJvZHVjdCgiRCIsIDEwMCkKdmFsIEUgPSBQcm9kdWN0KCJFIiwgMTAwKQp2YWwgRiA9IFByb2R1Y3QoIkYiLCAxMDApCnZhbCBHID0gUHJvZHVjdCgiRyIsIDEwMCkKdmFsIEggPSBQcm9kdWN0KCJIIiwgMTAwKQp2YWwgSSA9IFByb2R1Y3QoIkkiLCAxMDApCnZhbCBKID0gUHJvZHVjdCgiSiIsIDEwMCkKdmFsIEsgPSBQcm9kdWN0KCJLIiwgMTAwKQp2YWwgTCA9IFByb2R1Y3QoIkwiLCAxMDApCnZhbCBNID0gUHJvZHVjdCgiTSIsIDEwMCkKCnZhbCBkaXNjb3VudHMgPSBTZXEobmV3IENvbmNyZXRlUHJvZHVjdHNEaXNjb3VudChTZXQoQSwgQiksIDAuMSksCiAgICAgICAgICAgICAgICAgICAgbmV3IENvbmNyZXRlUHJvZHVjdHNEaXNjb3VudChTZXQoRCwgRSksIDAuMDUpLAogICAgICAgICAgICAgICAgICAgIG5ldyBDb25jcmV0ZVByb2R1Y3RzRGlzY291bnQoU2V0KEUsIEYsIEcpLCAwLjA1KSwKICAgICAgICAgICAgICAgICAgICBuZXcgT3JkZXJDb3VudERpc2NvdW50KHsgXyA9PSAzfSwgU2V0KEEsIEMpLCAwLjA1KSwKICAgICAgICAgICAgICAgICAgICBuZXcgT3JkZXJDb3VudERpc2NvdW50KHsgXyA9PSA0fSwgU2V0KEEsIEMpLCAwLjEpLAogICAgICAgICAgICAgICAgICAgIG5ldyBPcmRlckNvdW50RGlzY291bnQoeyBfID49IDV9LCBTZXQoQSwgQyksIDAuMikpCgpwcmludGxuKGNhbGN1bGF0ZShTZXEoQSwgQiwgQSwgQiwgSywgSywgSywgRSwgRiwgRyksIGRpc2NvdW50cykpCn0=