Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
Empty file added .github/.keep
Empty file.
30 changes: 30 additions & 0 deletions src/main/java/com/example/Category.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example;

import java.util.HashMap;

public class Category {
public String name;
static HashMap<String, Category> categories = new HashMap<>();

private Category(String categoryName) {
name = categoryName;
name = name.substring(0, 1).toUpperCase()
+ name.substring(1).toLowerCase();
}

public static Category of(String name) {
if (name == null) {
throw new IllegalArgumentException("Category name can't be null");
} else if (name.isBlank())
throw new IllegalArgumentException("Category name can't be blank");
if (categories.containsKey(name))
return categories.get(name);
categories.put(name, new Category(name));
return categories.get(name);
}

public String getName() {
return name;
}
}

40 changes: 40 additions & 0 deletions src/main/java/com/example/ElectronicsProduct.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.example;

import java.math.BigDecimal;
import java.util.UUID;

public class ElectronicsProduct extends Product implements Shippable {
int warrantyMonths;
BigDecimal weight;
BigDecimal basePrice = BigDecimal.valueOf(79);
BigDecimal addPrice = BigDecimal.valueOf(49);

public ElectronicsProduct(UUID id, String name, Category category, BigDecimal price, int warranty, BigDecimal weight) {
super(id, name, category, price);
this.warrantyMonths = warranty;
if (warrantyMonths < 0)
throw new IllegalArgumentException("Warranty months cannot be negative.");
this.weight = weight;
}

public int getWarrantyMonths() {
return warrantyMonths;
}

@Override
public String productDetails() {
return "Electronics: " + name + ", Warranty: " + warrantyMonths + " months";
}

@Override
public double weight() {
return weight.doubleValue();
}

@Override
public BigDecimal calculateShippingCost() {
return (weight.compareTo(BigDecimal.valueOf(5.0)) >= 0)
? basePrice.add(addPrice)
: basePrice;
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/example/FoodProduct.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;

public class FoodProduct extends Product implements Perishable, Shippable {
public LocalDate expirationDate;
public BigDecimal weight;

public FoodProduct(UUID id, String name, Category category, BigDecimal price, LocalDate expirationDate, BigDecimal weight) {
super(id, name, category, price);
this.expirationDate = expirationDate;
if (weight.compareTo(BigDecimal.ZERO) < 0)
throw new IllegalArgumentException("Weight cannot be negative.");
this.weight = weight;
}

@Override
public LocalDate expirationDate() {
return expirationDate;
}

@Override
public String productDetails() {
return "Food: " + name + ", Expires: " + expirationDate;
}

@Override
public double weight() {
return weight.doubleValue();
}

@Override
public BigDecimal calculateShippingCost() {
return weight.multiply(BigDecimal.valueOf(50));
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/example/Perishable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example;

import java.time.LocalDate;

interface Perishable {
LocalDate expirationDate();
default boolean isExpired() {
return LocalDate.now().isAfter(expirationDate());
}
}
42 changes: 42 additions & 0 deletions src/main/java/com/example/Product.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.example;

import java.math.BigDecimal;
import java.util.UUID;

public abstract class Product {
UUID uuid;
String name;
Category category;
BigDecimal price;

public Product(UUID uuid, String name, Category category, BigDecimal price) {
this.uuid = uuid;
this.name = name;
this.category = category;
if(price.compareTo(BigDecimal.ZERO) < 0)
throw new IllegalArgumentException("Price cannot be negative.");
this.price = price;
}

public BigDecimal price() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

public UUID uuid() {
return uuid;
}

public String name() {
return name;
}

public Category category() {
return category;
}

public abstract String productDetails();
}
8 changes: 8 additions & 0 deletions src/main/java/com/example/Shippable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example;

import java.math.BigDecimal;

public interface Shippable {
BigDecimal calculateShippingCost();
double weight();
}
114 changes: 114 additions & 0 deletions src/main/java/com/example/Warehouse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.example;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

public class Warehouse {
static HashMap<String, Warehouse> warehouses = new HashMap<>();
HashMap<UUID, Product> products;
List<Shippable> shippables;
List<Perishable> perishables;
HashMap<Optional<Product>, HashMap<LocalDateTime, BigDecimal>> changedProducts;

private Warehouse() {
this.products = new HashMap<>();
this.shippables = new ArrayList<>();
this.perishables = new ArrayList<>();
this.changedProducts = new HashMap<>();
}

public List<Product> getProducts(){
return List.copyOf(products.values());
}

public List<Shippable> shippableProducts() {
return shippables;
}

public void clearProducts(){
products.clear();
Category.categories.clear();
shippables.clear();
perishables.clear();
}

public boolean isEmpty(){
return products.isEmpty();
}

public void addProduct(Product product){
if (product == null)
throw new IllegalArgumentException("Product cannot be null.");
if (getProductById(product.uuid).isPresent())
throw new IllegalArgumentException("Product with that id already exists, use updateProduct for updates.");
products.put(product.uuid, product);
if(product instanceof Shippable)
shippables.add((Shippable) product);
if(product instanceof Perishable)
perishables.add((Perishable) product);
}

public void remove(UUID id){
products.remove(id);
}

public Optional<Product> getProductById(UUID id){
if (products.isEmpty())
return Optional.empty();
return (Optional.ofNullable(products.get(id)));
}

public static synchronized Warehouse getInstance(String string) {
if (!warehouses.containsKey(string)) {
warehouses.put(string, new Warehouse());
}
return warehouses.get(string);
}

public static synchronized Warehouse getInstance() {
if(!warehouses.containsKey("default"))
warehouses.put("default", new Warehouse());
return warehouses.get("default");
}

public Map<Category, List<Product>> getProductsGroupedByCategories() {
if(products == null || products.isEmpty()){
return Collections.emptyMap();
}
HashMap<Category, List<Product>> result = new HashMap<>();
var tempMap = Category.categories.entrySet();
var productsList = new ArrayList<>(products.values());
for(var category : tempMap){
List<Product> tempList = new ArrayList<>();
productsList.stream()
.filter(element -> element.category.equals(category.getValue()))
.forEach(tempList::add);
result.put(category.getValue(), tempList);
}
return result;
}

public void updateProductPrice(UUID id, BigDecimal newPrice){
var product = getProductById(id);
if(product.isPresent()) {
var tempMap = new HashMap<LocalDateTime, BigDecimal>();
tempMap.put(LocalDateTime.now(), product.get().price);
changedProducts.put(product, tempMap);
product.get().setPrice(newPrice);
}
else
throw new NoSuchElementException("Product not found with id:" + id);
}

public Map<Optional<Product>, HashMap<LocalDateTime, BigDecimal>> getChangedProducts(){
return changedProducts;
}

public List<Perishable> expiredProducts() {
return perishables.stream()
.filter(Perishable::isExpired)
.toList();
}
}

48 changes: 34 additions & 14 deletions src/main/java/com/example/WarehouseAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,26 +145,46 @@ public Map<Category, BigDecimal> calculateWeightedAveragePriceByCategory() {
* @param standardDeviations threshold in standard deviations (e.g., 2.0)
* @return list of products considered outliers
*/


public List<Product> findPriceOutliers(double standardDeviations) {
List<Product> products = warehouse.getProducts();
int n = products.size();
if (n == 0) return List.of();
double sum = products.stream().map(Product::price).mapToDouble(bd -> bd.doubleValue()).sum();
double mean = sum / n;
double variance = products.stream()
List<Product> productList = warehouse.getProducts();
List<BigDecimal> products = productList.stream()
.map(Product::price)
.mapToDouble(bd -> Math.pow(bd.doubleValue() - mean, 2))
.sum() / n;
double std = Math.sqrt(variance);
double threshold = standardDeviations * std;
.sorted()
.toList();

var Q1 = getPercentile(products, 25);
var Q3 = getPercentile(products, 75);

var IQR = Q3.doubleValue()-Q1.doubleValue();
var lowRange = Q1.doubleValue()-standardDeviations*IQR;
var highRange = Q3.doubleValue()+standardDeviations*IQR;

List<Product> outliers = new ArrayList<>();
for (Product p : products) {
double diff = Math.abs(p.price().doubleValue() - mean);
if (diff > threshold) outliers.add(p);
for(Product p : productList){
if((p.price.doubleValue()>highRange) || (p.price.doubleValue()<lowRange))
outliers.add(p);
}
return outliers;
}



static <T extends Comparable<T>> T getPercentile(Collection<T> input, double percentile) {
if (input == null || input.isEmpty()) {
throw new IllegalArgumentException("The input dataset cannot be null or empty.");
}
if (percentile < 0 || percentile > 100) {
throw new IllegalArgumentException("Percentile must be between 0 and 100 inclusive.");
}
List<T> sortedList = input.stream()
.sorted()
.collect(Collectors.toList());

int rank = percentile == 0 ? 1 : (int) Math.ceil(percentile / 100.0 * input.size());
return sortedList.get(rank - 1);
}

/**
* Groups all shippable products into ShippingGroup buckets such that each group's total weight
* does not exceed the provided maximum. The goal is to minimize the number of groups and/or total
Expand Down